Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/playwright-core/src/server/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1015,14 +1015,14 @@ export function waitForSelectorTask(selector: SelectorInfo, state: 'attached' |
if (lastElement !== element) {
lastElement = element;
if (!element) {
progress.log(` selector did not resolve to any element`);
progress.log(` locator did not resolve to any element`);
} else {
if (elements.length > 1) {
if (strict)
throw injected.strictModeViolationError(parsed, elements);
progress.log(` selector resolved to ${elements.length} elements. Proceeding with the first one.`);
progress.log(` locator resolved to ${elements.length} elements. Proceeding with the first one.`);
}
progress.log(` selector resolved to ${visible ? 'visible' : 'hidden'} ${injected.previewNode(element)}`);
progress.log(` locator resolved to ${visible ? 'visible' : 'hidden'} ${injected.previewNode(element)}`);
}
}

Expand Down
16 changes: 8 additions & 8 deletions packages/playwright-core/src/server/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ export class Frame extends SdkObject {
if (!['attached', 'detached', 'visible', 'hidden'].includes(state))
throw new Error(`state: expected one of (attached|detached|visible|hidden)`);
return controller.run(async progress => {
progress.log(`waiting for "${this._asLocator(selector)}"${state === 'attached' ? '' : ' to be ' + state}`);
progress.log(`waiting for ${this._asLocator(selector)}${state === 'attached' ? '' : ' to be ' + state}`);
return this.retryWithProgress(progress, selector, options, async (selectorInFrame, continuePolling) => {
// Be careful, |this| can be different from |frame|.
// We did not pass omitAttached, so it is non-null.
Expand Down Expand Up @@ -1114,7 +1114,7 @@ export class Frame extends SdkObject {
const { frame, info } = selectorInFrame!;
// Be careful, |this| can be different from |frame|.
const task = dom.waitForSelectorTask(info, 'attached');
progress.log(`waiting for "${this._asLocator(selector)}"`);
progress.log(`waiting for ${this._asLocator(selector)}`);
const handle = await frame._scheduleRerunnableHandleTask(progress, info.world, task);
const element = handle.asElement() as dom.ElementHandle<Element>;
try {
Expand Down Expand Up @@ -1268,7 +1268,7 @@ export class Frame extends SdkObject {
async isVisible(metadata: CallMetadata, selector: string, options: types.StrictOptions = {}): Promise<boolean> {
const controller = new ProgressController(metadata, this);
return controller.run(async progress => {
progress.log(` checking visibility of "${selector}"`);
progress.log(` checking visibility of ${this._asLocator(selector)}`);
const pair = await this.resolveFrameForSelectorNoWait(selector, options);
if (!pair)
return false;
Expand Down Expand Up @@ -1515,7 +1515,7 @@ export class Frame extends SdkObject {
const callbackText = body.toString();
return this.retryWithProgress(progress, selector, options, async selectorInFrame => {
// Be careful, |this| can be different from |frame|.
progress.log(`waiting for "${this._asLocator(selector)}"`);
progress.log(`waiting for ${this._asLocator(selector)}`);
const { frame, info } = selectorInFrame || { frame: this, info: { parsed: { parts: [{ name: 'internal:control', body: 'return-empty', source: 'internal:control=return-empty' }] }, world: 'utility', strict: !!options.strict } };
return await frame._scheduleRerunnableTaskInFrame(progress, info, callbackText, taskData, options);
});
Expand All @@ -1542,12 +1542,12 @@ export class Frame extends SdkObject {
if (querySelectorAll) {
elements = injected.querySelectorAll(info.parsed, document);
element = elements[0];
progress.logRepeating(` selector resolved to ${elements.length} element${elements.length === 1 ? '' : 's'}`);
progress.logRepeating(` locator resolved to ${elements.length} element${elements.length === 1 ? '' : 's'}`);
} else {
element = injected.querySelector(info.parsed, document, info.strict);
elements = element ? [element] : [];
if (element)
progress.logRepeating(` selector resolved to ${injected.previewNode(element)}`);
progress.logRepeating(` locator resolved to ${injected.previewNode(element)}`);
}

if (!element && !omitAttached)
Expand Down Expand Up @@ -1667,7 +1667,7 @@ export class Frame extends SdkObject {
for (let i = 0; i < frameChunks.length - 1 && progress.isRunning(); ++i) {
const info = this._page.parseSelector(frameChunks[i], options);
const task = dom.waitForSelectorTask(info, 'attached', false, i === 0 ? scope : undefined);
progress.log(` waiting for frame "${stringifySelector(frameChunks[i])}"`);
progress.log(` waiting for frameLocator('${stringifySelector(frameChunks[i])}')`);
const handle = i === 0 && scope ? await frame._runWaitForSelectorTaskOnce(progress, stringifySelector(info.parsed), info.world, task)
: await frame._scheduleRerunnableHandleTask(progress, info.world, task);
const element = handle.asElement() as dom.ElementHandle<Element>;
Expand Down Expand Up @@ -1710,7 +1710,7 @@ export class Frame extends SdkObject {
progress.cleanupWhenAborted(() => result.dispose());
return result;
} catch (e) {
throw new Error(`Error: frame navigated while waiting for "${this._asLocator(selector)}"`);
throw new Error(`Error: frame navigated while waiting for ${this._asLocator(selector)}`);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1018,10 +1018,10 @@ export class InjectedScript {
preview: this.previewNode(m),
selector: this.generateSelector(m),
}));
const lines = infos.map((info, i) => `\n ${i + 1}) ${info.preview} aka page.${asLocator(this._sdkLanguage, info.selector)}`);
const lines = infos.map((info, i) => `\n ${i + 1}) ${info.preview} aka ${asLocator(this._sdkLanguage, info.selector)}`);
if (infos.length < matches.length)
lines.push('\n ...');
return this.createStacklessError(`strict mode violation: "${stringifySelector(selector)}" resolved to ${matches.length} elements:${lines.join('')}\n`);
return this.createStacklessError(`strict mode violation: ${asLocator(this._sdkLanguage, stringifySelector(selector))} resolved to ${matches.length} elements:${lines.join('')}\n`);
}

createStacklessError(message: string): Error {
Expand Down
4 changes: 2 additions & 2 deletions tests/library/inspector/pause.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,8 @@ it.describe('pause', () => {
expect(await sanitizeLog(recorderPage)).toEqual([
'page.pause- XXms',
'page.getByRole(\'button\').isChecked()- XXms',
'waiting for \"getByRole(\'button\')"',
'selector resolved to <button onclick=\"console.log(1)\">Submit</button>',
'waiting for getByRole(\'button\')',
'locator resolved to <button onclick=\"console.log(1)\">Submit</button>',
'error: Error: Not a checkbox or radio button',
]);
const error = await scriptPromise;
Expand Down
14 changes: 7 additions & 7 deletions tests/page/expect-boolean.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ test.describe('toBeChecked', () => {
const locator = page.locator('input');
const error = await expect(locator).not.toBeChecked({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain(`expect.toBeChecked with timeout 1000ms`);
expect(error.message).toContain(`selector resolved to <input checked type="checkbox"/>`);
expect(error.message).toContain(`locator resolved to <input checked type="checkbox"/>`);
});

test('fail with checked:false', async ({ page }) => {
Expand All @@ -74,7 +74,7 @@ test.describe('toBeChecked', () => {
const locator2 = page.locator('input2');
const error = await expect(locator2).not.toBeChecked({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain(`expect.toBeChecked with timeout 1000ms`);
expect(error.message).toContain('waiting for "locator(\'input2\')"');
expect(error.message).toContain('waiting for locator(\'input2\')');
});

test('with role', async ({ page }) => {
Expand Down Expand Up @@ -143,7 +143,7 @@ test.describe('toBeEnabled', () => {
await page.setContent('<button disabled>Text</button>');
const locator = page.locator('button');
const error = await expect(locator).toBeEnabled({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain(`selector resolved to <button disabled>Text</button>`);
expect(error.message).toContain(`locator resolved to <button disabled>Text</button>`);
});

test('eventually', async ({ page }) => {
Expand Down Expand Up @@ -272,14 +272,14 @@ test.describe('toBeVisible', () => {
await page.setContent('<button style="display: none"></button>');
const locator = page.locator('button');
const error = await expect(locator).toBeVisible({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain(`selector resolved to <button></button>`);
expect(error.message).toContain(`locator resolved to <button></button>`);
});

test('fail with not', async ({ page }) => {
await page.setContent('<input></input>');
const locator = page.locator('input');
const error = await expect(locator).not.toBeVisible({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain(`selector resolved to <input/>`);
expect(error.message).toContain(`locator resolved to <input/>`);
});
});

Expand Down Expand Up @@ -324,14 +324,14 @@ test.describe('toBeHidden', () => {
await page.setContent('<input></input>');
const locator = page.locator('input');
const error = await expect(locator).toBeHidden({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain(`selector resolved to <input/>`);
expect(error.message).toContain(`locator resolved to <input/>`);
});

test('fail with not', async ({ page }) => {
await page.setContent('<button style="display: none"></button>');
const locator = page.locator('button');
const error = await expect(locator).not.toBeHidden({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain(`selector resolved to <button></button>`);
expect(error.message).toContain(`locator resolved to <button></button>`);
});

test('fail with not when nothing matching', async ({ page }) => {
Expand Down
8 changes: 4 additions & 4 deletions tests/page/expect-to-have-text.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ test.describe('not.toHaveText', () => {
const error = await expect(page.locator('span')).not.toHaveText('hello', { timeout: 1000 }).catch(e => e);
expect(stripAnsi(error.message)).toContain('Expected string: not "hello"');
expect(stripAnsi(error.message)).toContain('Received string: ""');
expect(stripAnsi(error.message)).toContain('waiting for "locator(\'span\')"');
expect(stripAnsi(error.message)).toContain('waiting for locator(\'span\')');
});
});

Expand Down Expand Up @@ -217,15 +217,15 @@ test.describe('toHaveText with array', () => {
const error = await expect(locator).toHaveText(['Text 1', /Text \d/, 'Extra'], { timeout: 1000 }).catch(e => e);
expect(stripAnsi(error.message)).toContain('- "Extra"');
expect(error.message).toContain('expect.toHaveText with timeout 1000ms');
expect(error.message).toContain('waiting for "locator(\'div\')"');
expect(error.message).toContain('selector resolved to 2 elements');
expect(error.message).toContain('waiting for locator(\'div\')');
expect(error.message).toContain('locator resolved to 2 elements');
});

test('fail on repeating array matchers', async ({ page }) => {
await page.setContent('<div>KekFoo</div>');
const locator = page.locator('div');
const error = await expect(locator).toContainText(['KekFoo', 'KekFoo', 'KekFoo'], { timeout: 1000 }).catch(e => e);
expect(error.message).toContain('selector resolved to 1 element');
expect(error.message).toContain('locator resolved to 1 element');
});
});

Expand Down
2 changes: 1 addition & 1 deletion tests/page/locator-convenience.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ it('innerText should produce log', async ({ page, server }) => {
await page.setContent(`<div>Hello</div>`);
const locator = page.locator('span');
const error = await locator.innerText({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain('waiting for "locator(\'span\')"');
expect(error.message).toContain('waiting for locator(\'span\')');
});

it('textContent should work', async ({ page, server }) => {
Expand Down
4 changes: 2 additions & 2 deletions tests/page/locator-frame.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ it('should work for $ and $$', async ({ page, server }) => {
it('should wait for frame', async ({ page, server }) => {
await page.goto(server.EMPTY_PAGE);
const error = await page.frameLocator('iframe').locator('span').click({ timeout: 1000 }).catch(e => e);
expect(error.message).toContain('waiting for frame "iframe"');
expect(error.message).toContain('waiting for frameLocator(\'iframe\')');
});

it('should wait for frame 2', async ({ page, server }) => {
Expand Down Expand Up @@ -225,7 +225,7 @@ it('locator.frameLocator should throw on ambiguity', async ({ page, server }) =>
await page.goto(server.EMPTY_PAGE);
const button = page.locator('body').frameLocator('iframe').locator('button');
const error = await button.waitFor().catch(e => e);
expect(error.message).toContain('Error: strict mode violation: "body >> iframe" resolved to 3 elements');
expect(error.message).toContain(`Error: strict mode violation: locator('body').locator('iframe') resolved to 3 elements`);
});

it('locator.frameLocator should not throw on first/last/nth', async ({ page, server }) => {
Expand Down
12 changes: 6 additions & 6 deletions tests/page/page-strict.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ it('should fail page.textContent in strict mode', async ({ page }) => {
await page.setContent(`<span>span1</span><div><span>target</span></div>`);
const error = await page.textContent('span', { strict: true }).catch(e => e);
expect(error.message).toContain('strict mode violation');
expect(error.message).toContain(`1) <span>span1</span> aka page.getByText('span1')`);
expect(error.message).toContain(`2) <span>target</span> aka page.getByText('target')`);
expect(error.message).toContain(`1) <span>span1</span> aka getByText('span1')`);
expect(error.message).toContain(`2) <span>target</span> aka getByText('target')`);
});

it('should fail page.getAttribute in strict mode', async ({ page }) => {
Expand All @@ -34,8 +34,8 @@ it('should fail page.fill in strict mode', async ({ page }) => {
await page.setContent(`<input></input><div><input></input></div>`);
const error = await page.fill('input', 'text', { strict: true }).catch(e => e);
expect(error.message).toContain('strict mode violation');
expect(error.message).toContain(`1) <input/> aka page.locator('input').first()`);
expect(error.message).toContain(`2) <input/> aka page.locator('div input')`);
expect(error.message).toContain(`1) <input/> aka locator('input').first()`);
expect(error.message).toContain(`2) <input/> aka locator('div input')`);
});

it('should fail page.$ in strict mode', async ({ page }) => {
Expand All @@ -54,8 +54,8 @@ it('should fail page.dispatchEvent in strict mode', async ({ page }) => {
await page.setContent(`<span></span><div><span></span></div>`);
const error = await page.dispatchEvent('span', 'click', {}, { strict: true }).catch(e => e);
expect(error.message).toContain('strict mode violation');
expect(error.message).toContain(`1) <span></span> aka page.locator('span').first()`);
expect(error.message).toContain(`2) <span></span> aka page.locator('div span')`);
expect(error.message).toContain(`1) <span></span> aka locator('span').first()`);
expect(error.message).toContain(`2) <span></span> aka locator('div span')`);
});

it('should properly format :nth-child() in strict mode message', async ({ page }) => {
Expand Down
19 changes: 9 additions & 10 deletions tests/page/page-wait-for-selector-1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ it('elementHandle.waitForSelector should throw on navigation', async ({ page, se
await page.evaluate(() => 1);
await page.goto(server.EMPTY_PAGE);
const error = await promise;
expect(error.message).toContain('Error: frame navigated while waiting for');
expect(error.message).toContain('"locator(\'span\')"');
expect(error.message).toContain('Error: frame navigated while waiting for locator(\'span\')');
});

it('should work with removed MutationObserver', async ({ page, server }) => {
Expand Down Expand Up @@ -134,10 +133,10 @@ it('should report logs while waiting for visible', async ({ page, server }) => {

const error = await watchdog.catch(e => e);
expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`);
expect(error.message).toContain(`waiting for "locator(\'div\')" to be visible`);
expect(error.message).toContain(`selector resolved to hidden <div id="mydiv" class="foo bar" foo="1234567890123456…>abcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvw…</div>`);
expect(error.message).toContain(`selector did not resolve to any element`);
expect(error.message).toContain(`selector resolved to hidden <div class="another"></div>`);
expect(error.message).toContain(`waiting for locator(\'div\') to be visible`);
expect(error.message).toContain(`locator resolved to hidden <div id="mydiv" class="foo bar" foo="1234567890123456…>abcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvw…</div>`);
expect(error.message).toContain(`locator did not resolve to any element`);
expect(error.message).toContain(`locator resolved to hidden <div class="another"></div>`);
});

it('should report logs while waiting for hidden', async ({ page, server }) => {
Expand Down Expand Up @@ -165,9 +164,9 @@ it('should report logs while waiting for hidden', async ({ page, server }) => {

const error = await watchdog.catch(e => e);
expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`);
expect(error.message).toContain(`waiting for "locator(\'div\')" to be hidden`);
expect(error.message).toContain(`selector resolved to visible <div id="mydiv" class="foo bar">hello</div>`);
expect(error.message).toContain(`selector resolved to visible <div class="another">hello</div>`);
expect(error.message).toContain(`waiting for locator(\'div\') to be hidden`);
expect(error.message).toContain(`locator resolved to visible <div id="mydiv" class="foo bar">hello</div>`);
expect(error.message).toContain(`locator resolved to visible <div class="another">hello</div>`);
});

it('should report logs when the selector resolves to multiple elements', async ({ page, server }) => {
Expand All @@ -183,7 +182,7 @@ it('should report logs when the selector resolves to multiple elements', async (
const error = await page.click('text=Reset', {
timeout: 1000
}).catch(e => e);
expect(error.toString()).toContain('selector resolved to 2 elements. Proceeding with the first one.');
expect(error.toString()).toContain('locator resolved to 2 elements. Proceeding with the first one.');
});

it('should resolve promise when node is added in shadow dom', async ({ page, server }) => {
Expand Down
Loading