diff --git a/docs/src/api/class-locatorassertions.md b/docs/src/api/class-locatorassertions.md index b4685e59848d3..935fa90ed2ce8 100644 --- a/docs/src/api/class-locatorassertions.md +++ b/docs/src/api/class-locatorassertions.md @@ -723,6 +723,9 @@ var locator = Page.Locator(".my-element"); await Expect(locator).ToBeVisibleAsync(); ``` +### option: LocatorAssertions.toBeVisible.visible +* since: v1.26 +- `visible` <[boolean]> ### option: LocatorAssertions.toBeVisible.timeout = %%-js-assertions-timeout-%% * since: v1.18 ### option: LocatorAssertions.toBeVisible.timeout = %%-csharp-java-python-assertions-timeout-%% diff --git a/packages/playwright-test/src/matchers/matchers.ts b/packages/playwright-test/src/matchers/matchers.ts index 233cc7eae3eff..3627a9fb3bf8d 100644 --- a/packages/playwright-test/src/matchers/matchers.ts +++ b/packages/playwright-test/src/matchers/matchers.ts @@ -110,10 +110,11 @@ export function toBeHidden( export function toBeVisible( this: ReturnType, locator: LocatorEx, - options?: { timeout?: number }, + options?: { visible?: boolean, timeout?: number }, ) { return toBeTruthy.call(this, 'toBeVisible', locator, 'Locator', async (isNot, timeout, customStackTrace) => { - return await locator._expect(customStackTrace, 'to.be.visible', { isNot, timeout }); + const visible = !options || options.visible === undefined || options.visible === true; + return await locator._expect(customStackTrace, visible ? 'to.be.visible' : 'to.be.hidden', { isNot, timeout }); }, options); } diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index d134ec7e36d48..4687c35e279b9 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -3369,6 +3369,8 @@ interface LocatorAssertions { * Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`. */ timeout?: number; + + visible?: boolean; }): Promise; /** diff --git a/tests/page/expect-boolean.spec.ts b/tests/page/expect-boolean.spec.ts index f339c1e5d7b41..205183aff3726 100644 --- a/tests/page/expect-boolean.spec.ts +++ b/tests/page/expect-boolean.spec.ts @@ -209,105 +209,127 @@ test('not.toBeDisabled div', async ({ page }) => { await expect(locator).not.toBeDisabled(); }); -test('toBeVisible', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).toBeVisible(); -}); +test.describe('toBeVisible', () => { + test('default', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).toBeVisible(); + }); -test('not.toBeVisible', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).not.toBeVisible(); -}); + test('with not', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).not.toBeVisible(); + }); -test('toBeHidden', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - await expect(locator).toBeHidden(); -}); + test('with visible:true', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).toBeVisible({ visible: true }); + }); -test('toBeHidden when nothing matches', async ({ page }) => { - await page.setContent('
'); - const locator = page.locator('button'); - await expect(locator).toBeHidden(); -}); + test('with visible:false', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).toBeVisible({ visible: false }); + }); -test('not.toBeHidden', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - await expect(locator).not.toBeHidden(); -}); + test('with not and visible:false', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).not.toBeVisible({ visible: false }); + }); -test('toBeVisible eventually', async ({ page }) => { - await page.setContent('
'); - const locator = page.locator('span'); - setTimeout(() => { - page.$eval('div', div => div.innerHTML = 'Hello').catch(() => {}); - }, 0); - await expect(locator).toBeVisible(); -}); + test('eventually', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('span'); + setTimeout(() => { + page.$eval('div', div => div.innerHTML = 'Hello').catch(() => {}); + }, 0); + await expect(locator).toBeVisible(); + }); -test('not.toBeHidden eventually', async ({ page }) => { - await page.setContent('
'); - const locator = page.locator('span'); - setTimeout(() => { - page.$eval('div', div => div.innerHTML = 'Hello').catch(() => {}); - }, 0); - await expect(locator).not.toBeHidden(); -}); + test('eventually with not', async ({ page }) => { + await page.setContent('
Hello
'); + const locator = page.locator('span'); + setTimeout(() => { + page.$eval('span', span => span.textContent = '').catch(() => {}); + }, 0); + await expect(locator).not.toBeVisible(); + }); -test('not.toBeVisible eventually', async ({ page }) => { - await page.setContent('
Hello
'); - const locator = page.locator('span'); - setTimeout(() => { - page.$eval('span', span => span.textContent = '').catch(() => {}); - }, 0); - await expect(locator).not.toBeVisible(); -}); + test('fail', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + const error = await expect(locator).toBeVisible({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`selector resolved to `); + }); -test('toBeHidden eventually', async ({ page }) => { - await page.setContent('
Hello
'); - const locator = page.locator('span'); - setTimeout(() => { - page.$eval('span', span => span.textContent = '').catch(() => {}); - }, 0); - await expect(locator).toBeHidden(); + test('fail with not', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + const error = await expect(locator).not.toBeVisible({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`selector resolved to `); + }); }); -test('toBeVisible fail', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - const error = await expect(locator).toBeVisible({ timeout: 1000 }).catch(e => e); - expect(error.message).toContain(`selector resolved to `); -}); +test.describe('toBeHidden', () => { + test('default', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + await expect(locator).toBeHidden(); + }); -test('not.toBeVisible fail', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - const error = await expect(locator).not.toBeVisible({ timeout: 1000 }).catch(e => e); - expect(error.message).toContain(`selector resolved to `); -}); + test('when nothing matches', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('button'); + await expect(locator).toBeHidden(); + }); -test('toBeHidden fail', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('input'); - const error = await expect(locator).toBeHidden({ timeout: 1000 }).catch(e => e); - expect(error.message).toContain(`selector resolved to `); -}); + test('with not', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + await expect(locator).not.toBeHidden(); + }); -test('not.toBeHidden fail', async ({ page }) => { - await page.setContent(''); - const locator = page.locator('button'); - const error = await expect(locator).not.toBeHidden({ timeout: 1000 }).catch(e => e); - expect(error.message).toContain(`selector resolved to `); -}); + test('eventually with not', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('span'); + setTimeout(() => { + page.$eval('div', div => div.innerHTML = 'Hello').catch(() => {}); + }, 0); + await expect(locator).not.toBeHidden(); + }); -test('not.toBeHidden fail not matching', async ({ page }) => { - await page.setContent('
'); - const locator = page.locator('button'); - const error = await expect(locator).not.toBeHidden({ timeout: 1000 }).catch(e => e); - expect(error.message).toContain(`expect.toBeHidden with timeout 1000ms`); + test('eventually', async ({ page }) => { + await page.setContent('
Hello
'); + const locator = page.locator('span'); + setTimeout(() => { + page.$eval('span', span => span.textContent = '').catch(() => {}); + }, 0); + await expect(locator).toBeHidden(); + }); + + test('fail', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('input'); + const error = await expect(locator).toBeHidden({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`selector resolved to `); + }); + + test('fail with not', async ({ page }) => { + await page.setContent(''); + const locator = page.locator('button'); + const error = await expect(locator).not.toBeHidden({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`selector resolved to `); + }); + + test('fail with not when nothing matching', async ({ page }) => { + await page.setContent('
'); + const locator = page.locator('button'); + const error = await expect(locator).not.toBeHidden({ timeout: 1000 }).catch(e => e); + expect(error.message).toContain(`expect.toBeHidden with timeout 1000ms`); + }); }); test('toBeFocused', async ({ page }) => { diff --git a/tests/playwright-test/expect.spec.ts b/tests/playwright-test/expect.spec.ts index dd8d12911635c..112b431351112 100644 --- a/tests/playwright-test/expect.spec.ts +++ b/tests/playwright-test/expect.spec.ts @@ -248,6 +248,9 @@ test('should propose only the relevant matchers when custom expect matcher class await test.expect(page.locator('foo')).not.toBeEditable(); await test.expect(page.locator('foo')).toBeEditable({ editable: false }); + + await test.expect(page.locator('foo')).toBeVisible(); + await test.expect(page.locator('foo')).not.toBeVisible({ visible: false }); }); ` });