From 4bba41ab8feb3f972aa9d27e1adfd2c8763d98e1 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Thu, 14 Jul 2022 22:03:37 +0200 Subject: [PATCH] Revert "feat(matchers): add toContainClass (#15491)" (#15670) This reverts commit e4debd0bf665ae69a6621768d17e739aef13be9a. --- docs/src/api/class-locatorassertions.md | 95 +------------------ .../src/server/injected/injectedScript.ts | 28 +----- packages/playwright-test/src/expect.ts | 2 - .../playwright-test/src/matchers/matchers.ts | 19 ---- packages/playwright-test/types/test.d.ts | 43 +-------- .../playwright.expect.misc.spec.ts | 54 ----------- 6 files changed, 7 insertions(+), 234 deletions(-) diff --git a/docs/src/api/class-locatorassertions.md b/docs/src/api/class-locatorassertions.md index ecc532d5c0..834b64bc1c 100644 --- a/docs/src/api/class-locatorassertions.md +++ b/docs/src/api/class-locatorassertions.md @@ -721,99 +721,6 @@ await Expect(locator).ToBeVisibleAsync(); ### option: LocatorAssertions.toBeVisible.timeout = %%-csharp-java-python-assertions-timeout-%% * since: v1.18 -## async method: LocatorAssertions.toContainClass -* since: v1.24 -* langs: - - alias-java: containsClass - -Ensures the [Locator] points to an element that contains the given CSS class (or multiple). -In contrast to [`method: LocatorAssertions.toHaveClass`] which requires that the [Locator] has exactly the provided classes, `toContainClass` verifies that the [Locator] has a subset (or all) of the given CSS classes. - -```html -
-
-
-
-``` - -```js -const locator = page.locator('#component'); -await expect(locator).toContainClass('bar baz'); // pass, both classes are on element -await expect(locator).toContainClass('ba'); // fail, element has no 'ba' class - -const itemLocator = page.locator('#component .item'); -await expect(itemLocator).toContainClass(['alice', 'bob']); // pass, first element has alice, second bob -await expect(itemLocator).toContainClass(['alice', 'bob carl']); // no carl class found on second item element -await expect(itemLocator).toContainClass(['alice', 'bob', 'foobar']); // we expect 3 elements with the item class, but there are only 2 -``` - -```java -Locator locator = page.locator("#component"); -assertThat(locator).containsClass("bar baz"); // pass, both classes are on element -assertThat(locator).containsClass("ba"); // fail, element has no 'ba' class - -Locator itemLocator = page.locator("#component .item"); -assertThat(itemLocator).toContainClass(new String[] {"alice", "bob"}); // pass, first element has alice, second bob -assertThat(itemLocator).toContainClass(new String[] {"alice", "bob carl"}); // no carl class found on second item element -assertThat(itemLocator).toContainClass(new String[] {"alice", "bob", "foobar"}); // we expect 3 elements with the item class, but there are only 2 -``` - -```python async -from playwright.async_api import expect - -locator = page.locator('#component') -expect(locator).to_contain_class('bar baz') # pass, both classes are on element -expect(locator).to_contain_class('ba') # fail, element has no 'ba' class - -item_locator = page.locator('#component .item') -expect(item_locator).to_contain_class(['alice', 'bob']) # pass, first element has alice, second bob -expect(item_locator).to_contain_class(['alice', 'bob carl']) # no carl class found on second item element -expect(item_locator).to_contain_class(['alice', 'bob', 'foobar']) # we expect 3 elements with the item class, but there are only 2 -``` - -```python sync -from playwright.sync_api import expect - -locator = page.locator('#component') -await expect(locator).to_contain_class('bar baz') # pass, both classes are on element -await expect(locator).to_contain_class('ba') # fail, element has no 'ba' class - -item_locator = page.locator('#component .item') -await expect(item_locator).to_contain_class(['alice', 'bob']) # pass, first element has alice, second bob -await expect(item_locator).to_contain_class(['alice', 'bob carl']) # no carl class found on second item element -await expect(item_locator).to_contain_class(['alice', 'bob', 'foobar']) # we expect 3 elements with the item class, but there are only 2 -``` - -```csharp -var locator = Page.Locator("#component"); -await Expect(locator).ToContainClassAsync("bar baz"); // pass, both classes are on element -await Expect(locator).ToContainClassAsync("ba"); // fail, element has no "ba" class - -var itemLocator = page.locator("#component .item"); -await Expect(itemLocator).ToContainClassAsync(new string[]{"alice", "bob"}); // pass, first element has alice, second bob -await Expect(itemLocator).ToContainClassAsync(new string[]{"alice", "bob carl"}); // no carl class found on second item element -await Expect(itemLocator).ToContainClassAsync(new string[]{"alice", "bob", "foobar"}); // we expect 3 elements with the item class, but there are only 2 -``` - -Note that locator must point to a single element when passing a string or to multiple elements when passing an array. - -### param: LocatorAssertions.toContainClass.expected -* since: v1.24 -- `expected` <[string]|[Array]<[string]>> - -Expected classnames, whitespace separated. When passing an array, the given classes must be present on the locator elements. - -### option: LocatorAssertions.toContainClass.ignoreCase -* since: v1.24 -- `ignoreCase` <[boolean]> - -Whether to perform case-insensitive match. - -### option: LocatorAssertions.toContainClass.timeout = %%-js-assertions-timeout-%% -* since: v1.24 -### option: LocatorAssertions.toContainClass.timeout = %%-csharp-java-python-assertions-timeout-%% -* since: v1.24 - ## async method: LocatorAssertions.toContainText * since: v1.20 * langs: @@ -977,7 +884,7 @@ Expected attribute value. - alias-java: hasClass Ensures the [Locator] points to an element with given CSS classes. This needs to be a full match -or using a relaxed regular expression. For matching partial class names, use [`method: LocatorAssertions.toContainClass`]. +or using a relaxed regular expression. ```html
diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts index 030098fae0..d1e6538458 100644 --- a/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/packages/playwright-core/src/server/injected/injectedScript.ts @@ -1071,7 +1071,7 @@ export class InjectedScript { let received: string | undefined; if (expression === 'to.have.attribute') { received = element.getAttribute(options.expressionArg) || ''; - } else if (expression === 'to.have.class' || expression === 'to.contain.class') { + } else if (expression === 'to.have.class') { received = element.classList.toString(); } else if (expression === 'to.have.css') { received = window.getComputedStyle(element).getPropertyValue(options.expressionArg); @@ -1092,9 +1092,7 @@ export class InjectedScript { if (received !== undefined && options.expectedText) { const matcher = new ExpectedTextMatcher(options.expectedText[0]); - return { received, matches: matcher.matches(received, { - toContainClass: expression === 'to.contain.class', - }) }; + return { received, matches: matcher.matches(received) }; } } @@ -1114,7 +1112,7 @@ export class InjectedScript { let received: string[] | undefined; if (expression === 'to.have.text.array' || expression === 'to.contain.text.array') received = elements.map(e => options.useInnerText ? (e as HTMLElement).innerText : e.textContent || ''); - else if (expression === 'to.have.class.array' || expression === 'to.contain.class.array') + else if (expression === 'to.have.class.array') received = elements.map(e => e.classList.toString()); if (received && options.expectedText) { @@ -1128,9 +1126,7 @@ export class InjectedScript { 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], { - toContainClass: expression === 'to.contain.class.array', - })) + if (matchers[mIndex].matches(received[rIndex])) ++mIndex; ++rIndex; } @@ -1265,9 +1261,7 @@ class ExpectedTextMatcher { } } - matches(text: string, { toContainClass }: { toContainClass?: boolean } = {}): boolean { - if (toContainClass) - return this.matchesClassList(text); + matches(text: string): boolean { if (!this._regex) text = this.normalize(text)!; if (this._string !== undefined) @@ -1279,18 +1273,6 @@ class ExpectedTextMatcher { return false; } - private matchesClassList(received: string): boolean { - const expected = this.normalizeClassList(this._string || ''); - if (expected.length === 0) - return false; - const normalizedReceived = this.normalizeClassList(received); - return expected.every(classListEntry => normalizedReceived.includes(classListEntry)); - } - - private normalizeClassList(classList: string): string[] { - return classList.trim().split(/\s+/g).map(c => this.normalize(c)).filter(c => c) as string[]; - } - private normalize(s: string | undefined): string | undefined { if (!s) return s; diff --git a/packages/playwright-test/src/expect.ts b/packages/playwright-test/src/expect.ts index d96af9f6a3..eb17f3b7d5 100644 --- a/packages/playwright-test/src/expect.ts +++ b/packages/playwright-test/src/expect.ts @@ -29,7 +29,6 @@ import { toContainText, toHaveAttribute, toHaveClass, - toContainClass, toHaveCount, toHaveCSS, toHaveId, @@ -135,7 +134,6 @@ const customMatchers = { toContainText, toHaveAttribute, toHaveClass, - toContainClass, toHaveCount, toHaveCSS, toHaveId, diff --git a/packages/playwright-test/src/matchers/matchers.ts b/packages/playwright-test/src/matchers/matchers.ts index 01c8b9111b..c45f4b4ad8 100644 --- a/packages/playwright-test/src/matchers/matchers.ts +++ b/packages/playwright-test/src/matchers/matchers.ts @@ -164,25 +164,6 @@ export function toHaveClass( } } -export function toContainClass( - this: ReturnType, - locator: LocatorEx, - expected: string | string[], - options?: { timeout?: number, ignoreCase?: boolean }, -) { - if (Array.isArray(expected)) { - return toEqual.call(this, 'toContainClass', locator, 'Locator', async (isNot, timeout, customStackTrace) => { - const expectedText = toExpectedTextValues(expected, { ignoreCase: options?.ignoreCase }); - return await locator._expect(customStackTrace, 'to.contain.class.array', { expectedText, isNot, timeout }); - }, expected, options); - } else { - return toMatchText.call(this, 'toContainClass', locator, 'Locator', async (isNot, timeout, customStackTrace) => { - const expectedText = toExpectedTextValues([expected], { ignoreCase: options?.ignoreCase }); - return await locator._expect(customStackTrace, 'to.contain.class', { expectedText, isNot, timeout }); - }, expected, options); - } -} - export function toHaveCount( this: ReturnType, locator: LocatorEx, diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index e1f4310f0e..102f374d98 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -3327,46 +3327,6 @@ interface LocatorAssertions { timeout?: number; }): Promise; - /** - * Ensures the [Locator] points to an element that contains the given CSS class (or multiple). In contrast to - * [locatorAssertions.toHaveClass(expected[, options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-class) - * which requires that the [Locator] has exactly the provided classes, `toContainClass` verifies that the [Locator] has a - * subset (or all) of the given CSS classes. - * - * ```html - *
- *
- *
- *
- * ``` - * - * ```js - * const locator = page.locator('#component'); - * await expect(locator).toContainClass('bar baz'); // pass, both classes are on element - * await expect(locator).toContainClass('ba'); // fail, element has no 'ba' class - * - * const itemLocator = page.locator('#component .item'); - * await expect(itemLocator).toContainClass(['alice', 'bob']); // pass, first element has alice, second bob - * await expect(itemLocator).toContainClass(['alice', 'bob carl']); // no carl class found on second item element - * await expect(itemLocator).toContainClass(['alice', 'bob', 'foobar']); // we expect 3 elements with the item class, but there are only 2 - * ``` - * - * Note that locator must point to a single element when passing a string or to multiple elements when passing an array. - * @param expected Expected classnames, whitespace separated. When passing an array, the given classes must be present on the locator elements. - * @param options - */ - toContainClass(expected: string|Array, options?: { - /** - * Whether to perform case-insensitive match. - */ - ignoreCase?: boolean; - - /** - * Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`. - */ - timeout?: number; - }): Promise; - /** * Ensures the [Locator] points to an element that contains the given text. You can use regular expressions for the value * as well. @@ -3426,8 +3386,7 @@ interface LocatorAssertions { /** * Ensures the [Locator] points to an element with given CSS classes. This needs to be a full match or using a relaxed - * regular expression. For matching partial class names, use - * [locatorAssertions.toContainClass(expected[, options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-contain-class). + * regular expression. * * ```html *
diff --git a/tests/playwright-test/playwright.expect.misc.spec.ts b/tests/playwright-test/playwright.expect.misc.spec.ts index 1fffc813a4..b19ecda9c5 100644 --- a/tests/playwright-test/playwright.expect.misc.spec.ts +++ b/tests/playwright-test/playwright.expect.misc.spec.ts @@ -262,60 +262,6 @@ test('should support toHaveClass w/ array', async ({ runInlineTest }) => { expect(result.exitCode).toBe(1); }); -test('should support toContainClass', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test('pass', async ({ page }) => { - await page.setContent(\` -
- - -
- \`); - const locator = page.locator('div'); - await expect(locator).toContainClass('foo'); - // Leading/trailing whitespace - await expect(locator).toContainClass(' foo '); - // empty should not pass - await expect(locator).not.toContainClass(''); - await expect(locator).toContainClass('bar'); - await expect(locator).toContainClass('baz'); - await expect(locator).toContainClass('foo baz'); - await expect(locator).toContainClass('baz foo'); - await expect(locator).not.toContainClass('ba'); - - await expect(locator).toContainClass('BAZ FoO', { ignoreCase: true }); - await expect(locator).not.toContainClass('BAZ'); - - const locatorSpan = page.locator('div span'); - await expect(locatorSpan).toContainClass(['alice baz', 'bob']); - await expect(locatorSpan).not.toContainClass(['alice', 'alice']); - }); - - test('fail', async ({ page }) => { - await page.setContent('
'); - const locator = page.locator('div'); - await expect(locator).toContainClass('foo', { timeout: 1000 }); - }); - - test('fail length mismatch', async ({ page }) => { - await page.setContent('
'); - const locator = page.locator('div span'); - await expect(locator).toContainClass('alice', { timeout: 1000 }); - }); - `, - }, { workers: 1 }); - const output = stripAnsi(result.output); - expect(output).toContain('expect(locator).toContainClass'); - expect(output).toContain('Expected string: \"foo\"'); - expect(output).toContain('Received string: \"bar baz\"'); - expect(result.passed).toBe(1); - expect(result.failed).toBe(2); - expect(result.exitCode).toBe(1); -}); - test('should support toHaveTitle', async ({ runInlineTest }) => { const result = await runInlineTest({ 'a.test.ts': `