feat(text matchers): add ignoreOrder option
This commit is contained in:
parent
a6827772a5
commit
d6dffeb759
|
|
@ -217,6 +217,12 @@ Expected substring or RegExp or a list of those.
|
|||
|
||||
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
|
||||
|
||||
### option: LocatorAssertions.NotToContainText.ignoreOrder
|
||||
* since: v1.44
|
||||
- `ignoreOrder` <[boolean]>
|
||||
|
||||
Weather to ignore the order of the elements when comparing the text. This option is only available when the expected value is an array.
|
||||
|
||||
### option: LocatorAssertions.NotToContainText.useInnerText
|
||||
* since: v1.18
|
||||
- `useInnerText` <[boolean]>
|
||||
|
|
@ -358,6 +364,12 @@ Expected string or RegExp or a list of those.
|
|||
|
||||
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
|
||||
|
||||
### option: LocatorAssertions.NotToHaveText.ignoreOrder
|
||||
* since: v1.44
|
||||
- `ignoreOrder` <[boolean]>
|
||||
|
||||
Weather to ignore the order of the elements when comparing the text. This option is only available when the expected value is an array.
|
||||
|
||||
### option: LocatorAssertions.NotToHaveText.useInnerText
|
||||
* since: v1.18
|
||||
- `useInnerText` <[boolean]>
|
||||
|
|
@ -1095,6 +1107,12 @@ Expected substring or RegExp or a list of those.
|
|||
|
||||
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
|
||||
|
||||
### option: LocatorAssertions.toContainText.ignoreOrder
|
||||
* since: v1.44
|
||||
- `ignoreOrder` <[boolean]>
|
||||
|
||||
Weather to ignore the order of the elements when comparing the text. This option is only available when the expected value is an array.
|
||||
|
||||
### option: LocatorAssertions.toContainText.useInnerText
|
||||
* since: v1.18
|
||||
- `useInnerText` <[boolean]>
|
||||
|
|
@ -1775,6 +1793,12 @@ Expected string or RegExp or a list of those.
|
|||
|
||||
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
|
||||
|
||||
### option: LocatorAssertions.toHaveText.ignoreOrder
|
||||
* since: v1.44
|
||||
- `ignoreOrder` <[boolean]>
|
||||
|
||||
Weather to ignore the order of the elements when comparing the text. This option is only available when the expected value is an array.
|
||||
|
||||
### option: LocatorAssertions.toHaveText.useInnerText
|
||||
* since: v1.18
|
||||
- `useInnerText` <[boolean]>
|
||||
|
|
|
|||
|
|
@ -1660,6 +1660,7 @@ scheme.FrameExpectParams = tObject({
|
|||
expectedNumber: tOptional(tNumber),
|
||||
expectedValue: tOptional(tType('SerializedArgument')),
|
||||
useInnerText: tOptional(tBoolean),
|
||||
ignoreOrder: tOptional(tBoolean),
|
||||
isNot: tBoolean,
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1268,13 +1268,26 @@ export class InjectedScript {
|
|||
|
||||
// Each matcher should get a "received" that matches it, in order.
|
||||
const matchers = options.expectedText.map(e => new ExpectedTextMatcher(e));
|
||||
let mIndex = 0, rIndex = 0;
|
||||
while (mIndex < matchers.length && rIndex < received.length) {
|
||||
if (matchers[mIndex].matches(received[rIndex]))
|
||||
++mIndex;
|
||||
++rIndex;
|
||||
if (options.ignoreOrder) {
|
||||
const mReceivedIndexes = new Array<boolean>(received.length).fill(false);
|
||||
let mIndex = 0;
|
||||
while (mIndex < matchers.length) {
|
||||
for (let rIndex = 0; rIndex < received.length && mReceivedIndexes[mIndex]; rIndex++) {
|
||||
if (matchers[mIndex].matches(received[rIndex]))
|
||||
mReceivedIndexes[rIndex] = true;
|
||||
}
|
||||
mIndex++;
|
||||
}
|
||||
return { received, matches: mIndex === matchers.length };
|
||||
} else {
|
||||
let mIndex = 0, rIndex = 0;
|
||||
while (mIndex < matchers.length && rIndex < received.length) {
|
||||
if (matchers[mIndex].matches(received[rIndex]))
|
||||
++mIndex;
|
||||
++rIndex;
|
||||
}
|
||||
return { received, matches: mIndex === matchers.length };
|
||||
}
|
||||
return { received, matches: mIndex === matchers.length };
|
||||
}
|
||||
throw this.createStacklessError('Unknown expect matcher: ' + expression);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,12 +159,12 @@ export function toContainText(
|
|||
this: ExpectMatcherContext,
|
||||
locator: LocatorEx,
|
||||
expected: string | RegExp | (string | RegExp)[],
|
||||
options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean } = {},
|
||||
options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean, ignoreOrder?: boolean } = {},
|
||||
) {
|
||||
if (Array.isArray(expected)) {
|
||||
return toEqual.call(this, 'toContainText', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = toExpectedTextValues(expected, { matchSubstring: true, normalizeWhiteSpace: true, ignoreCase: options.ignoreCase });
|
||||
return await locator._expect('to.contain.text.array', { expectedText, isNot, useInnerText: options.useInnerText, timeout });
|
||||
return await locator._expect('to.contain.text.array', { expectedText, isNot, useInnerText: options.useInnerText, ignoreOrder: options.ignoreOrder, timeout });
|
||||
}, expected, { ...options, contains: true });
|
||||
} else {
|
||||
return toMatchText.call(this, 'toContainText', locator, 'Locator', async (isNot, timeout) => {
|
||||
|
|
@ -270,12 +270,12 @@ export function toHaveText(
|
|||
this: ExpectMatcherContext,
|
||||
locator: LocatorEx,
|
||||
expected: string | RegExp | (string | RegExp)[],
|
||||
options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean } = {},
|
||||
options: { timeout?: number, useInnerText?: boolean, ignoreCase?: boolean, ignoreOrder?: boolean } = {},
|
||||
) {
|
||||
if (Array.isArray(expected)) {
|
||||
return toEqual.call(this, 'toHaveText', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = toExpectedTextValues(expected, { normalizeWhiteSpace: true, ignoreCase: options.ignoreCase });
|
||||
return await locator._expect('to.have.text.array', { expectedText, isNot, useInnerText: options?.useInnerText, timeout });
|
||||
return await locator._expect('to.have.text.array', { expectedText, isNot, useInnerText: options?.useInnerText, ignoreOrder: options.ignoreOrder, timeout });
|
||||
}, expected, options);
|
||||
} else {
|
||||
return toMatchText.call(this, 'toHaveText', locator, 'Locator', async (isNot, timeout) => {
|
||||
|
|
|
|||
12
packages/playwright/types/test.d.ts
vendored
12
packages/playwright/types/test.d.ts
vendored
|
|
@ -6869,6 +6869,12 @@ interface LocatorAssertions {
|
|||
*/
|
||||
ignoreCase?: boolean;
|
||||
|
||||
/**
|
||||
* Weather to ignore the order of the elements when comparing the text. This option is only available when the
|
||||
* expected value is an array.
|
||||
*/
|
||||
ignoreOrder?: boolean;
|
||||
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestConfig.expect`.
|
||||
*/
|
||||
|
|
@ -7282,6 +7288,12 @@ interface LocatorAssertions {
|
|||
*/
|
||||
ignoreCase?: boolean;
|
||||
|
||||
/**
|
||||
* Weather to ignore the order of the elements when comparing the text. This option is only available when the
|
||||
* expected value is an array.
|
||||
*/
|
||||
ignoreOrder?: boolean;
|
||||
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestConfig.expect`.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2968,6 +2968,7 @@ export type FrameExpectParams = {
|
|||
expectedNumber?: number,
|
||||
expectedValue?: SerializedArgument,
|
||||
useInnerText?: boolean,
|
||||
ignoreOrder?: boolean,
|
||||
isNot: boolean,
|
||||
timeout?: number,
|
||||
};
|
||||
|
|
@ -2977,6 +2978,7 @@ export type FrameExpectOptions = {
|
|||
expectedNumber?: number,
|
||||
expectedValue?: SerializedArgument,
|
||||
useInnerText?: boolean,
|
||||
ignoreOrder?: boolean,
|
||||
timeout?: number,
|
||||
};
|
||||
export type FrameExpectResult = {
|
||||
|
|
|
|||
|
|
@ -2257,6 +2257,7 @@ Frame:
|
|||
expectedNumber: number?
|
||||
expectedValue: SerializedArgument?
|
||||
useInnerText: boolean?
|
||||
ignoreOrder: boolean?
|
||||
isNot: boolean
|
||||
timeout: number?
|
||||
returns:
|
||||
|
|
|
|||
|
|
@ -234,6 +234,19 @@ test.describe('toHaveText with array', () => {
|
|||
const error = await expect(locator).toContainText(['KekFoo', 'KekFoo', 'KekFoo'], { timeout: 1000 }).catch(e => e);
|
||||
expect(error.message).toContain('locator resolved to 1 element');
|
||||
});
|
||||
|
||||
test('ignoreOrder should work with strings', async ({ page }) => {
|
||||
await page.setContent('<div>Text 1</div><div>Text 2</div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toHaveText(['Text 2', 'Text 1'], { ignoreOrder: true });
|
||||
});
|
||||
|
||||
test('ignoreOrder should work with regex', async ({ page }) => {
|
||||
await page.setContent('<div>Text 1</div><div>Text 2</div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toHaveText([/Text 2/, /Text 1/], { ignoreOrder: true });
|
||||
await expect(expect(locator).toHaveText([/Text 2/, /Text 1/, /Text 43/], { ignoreOrder: true, timeout: 10 })).rejects.toThrow('Timed out 10ms waiting for');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('toContainText with array', () => {
|
||||
|
|
@ -251,4 +264,16 @@ test.describe('toContainText with array', () => {
|
|||
const error = await expect(locator).toContainText(['Text 2'], { timeout: 1000 }).catch(e => e);
|
||||
expect(stripAnsi(error.message)).toContain('- "Text 2"');
|
||||
});
|
||||
|
||||
test('ignoreOrder should work with strings', async ({ page }) => {
|
||||
await page.setContent('<div>Text 1</div><div>Text 2</div><div>Text 3</div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toContainText(['ext 2', 'ext 1'], { ignoreOrder: true });
|
||||
});
|
||||
|
||||
test('ignoreOrder should work with regex', async ({ page }) => {
|
||||
await page.setContent('<div>Text 1</div><div>Text 2</div><div>Text 3</div>');
|
||||
const locator = page.locator('div');
|
||||
await expect(locator).toContainText([/ext 2/, /ext 1/], { ignoreOrder: true });
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue