-
Notifications
You must be signed in to change notification settings - Fork 3.4k
test: cover waitforhelper network multiplier and navigation paths #2515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ZayanKhan-12
wants to merge
1
commit into
ChromeDevTools:main
Choose a base branch
from
ZayanKhan-12:chore/tests-waitforhelper
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+145
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -7,9 +7,33 @@ | |||||
| import assert from 'node:assert'; | ||||||
| import {describe, it} from 'node:test'; | ||||||
|
|
||||||
| import {getNetworkMultiplierFromString} from '../src/WaitForHelper.js'; | ||||||
|
|
||||||
| import {serverHooks} from './server.js'; | ||||||
| import {html, withMcpContext} from './utils.js'; | ||||||
|
|
||||||
| describe('getNetworkMultiplierFromString', () => { | ||||||
| it('maps each predefined network condition to its multiplier', () => { | ||||||
| assert.strictEqual(getNetworkMultiplierFromString('Fast 4G'), 1); | ||||||
| assert.strictEqual(getNetworkMultiplierFromString('Slow 4G'), 2.5); | ||||||
| assert.strictEqual(getNetworkMultiplierFromString('Fast 3G'), 5); | ||||||
| assert.strictEqual(getNetworkMultiplierFromString('Slow 3G'), 10); | ||||||
| }); | ||||||
|
|
||||||
| it('falls back to 1 for unknown condition strings', () => { | ||||||
| assert.strictEqual(getNetworkMultiplierFromString('2G'), 1); | ||||||
| assert.strictEqual(getNetworkMultiplierFromString('No emulation'), 1); | ||||||
| assert.strictEqual(getNetworkMultiplierFromString(''), 1); | ||||||
| }); | ||||||
|
|
||||||
| it('falls back to 1 when no condition is set', () => { | ||||||
| assert.strictEqual(getNetworkMultiplierFromString(null), 1); | ||||||
| }); | ||||||
| }); | ||||||
|
|
||||||
| describe('WaitForHelper', () => { | ||||||
| const server = serverHooks(); | ||||||
|
|
||||||
| it('does not stall when an action opens a dialog without handleDialog', async () => { | ||||||
| await withMcpContext(async (response, context) => { | ||||||
| const mcpPage = context.getSelectedMcpPage(); | ||||||
|
|
@@ -43,4 +67,125 @@ describe('WaitForHelper', () => { | |||||
| assert.throws(() => mcpPage.throwIfDialogOpen()); | ||||||
| }); | ||||||
| }); | ||||||
|
|
||||||
| it('reports navigatedToUrl when the action starts a cross-document navigation', async () => { | ||||||
| await withMcpContext(async (response, context) => { | ||||||
| const mcpPage = context.getSelectedMcpPage(); | ||||||
| server.addHtmlRoute('/target.html', html`<h1>target</h1>`); | ||||||
| const targetUrl = server.getRoute('/target.html'); | ||||||
|
|
||||||
| const result = await mcpPage.waitForEventsAfterAction(async () => { | ||||||
| await mcpPage.pptrPage.evaluate(url => { | ||||||
| // Navigate asynchronously so the evaluate call returns before the | ||||||
| // execution context is destroyed by the navigation. | ||||||
| setTimeout(() => { | ||||||
| window.location.href = url; | ||||||
| }, 0); | ||||||
| }, targetUrl); | ||||||
| }); | ||||||
|
|
||||||
| assert.strictEqual(result.navigatedToUrl, targetUrl); | ||||||
| assert.strictEqual(result.dialogHandled, false); | ||||||
| assert.strictEqual(mcpPage.pptrPage.url(), targetUrl); | ||||||
| }); | ||||||
| }); | ||||||
|
|
||||||
| it('resolves quickly without navigatedToUrl when no navigation happens', async () => { | ||||||
| await withMcpContext(async (response, context) => { | ||||||
| const mcpPage = context.getSelectedMcpPage(); | ||||||
| await mcpPage.pptrPage.setContent(html`<div id="root"></div>`); | ||||||
|
|
||||||
| const start = Date.now(); | ||||||
| const result = await mcpPage.waitForEventsAfterAction(async () => { | ||||||
| await mcpPage.pptrPage.evaluate(() => { | ||||||
| document.querySelector('#root')!.append('done'); | ||||||
| }); | ||||||
| }); | ||||||
| const elapsed = Date.now() - start; | ||||||
|
|
||||||
| assert.strictEqual(result.navigatedToUrl, undefined); | ||||||
| assert.strictEqual(result.dialogHandled, false); | ||||||
| // Expected wait is ~200ms (#expectNavigationIn 100ms + #stableDomFor | ||||||
| // 100ms). Assert we stayed below #stableDomTimeout/#navigationTimeout | ||||||
| // (3s each) to prove neither full timeout was consumed. | ||||||
| assert.ok( | ||||||
| elapsed < 2_000, | ||||||
| `expected a fast return without navigation, took ${elapsed}ms`, | ||||||
| ); | ||||||
| }); | ||||||
| }); | ||||||
|
|
||||||
| it('swallows navigation timeouts and still resolves with a result', async () => { | ||||||
| await withMcpContext(async (response, context) => { | ||||||
| const mcpPage = context.getSelectedMcpPage(); | ||||||
| server.addRoute('/hang.html', () => { | ||||||
| // Never respond so the started navigation cannot complete. | ||||||
| }); | ||||||
| const hangUrl = server.getRoute('/hang.html'); | ||||||
|
|
||||||
| const start = Date.now(); | ||||||
| const result = await mcpPage.waitForEventsAfterAction( | ||||||
| async () => { | ||||||
| await mcpPage.pptrPage.evaluate(url => { | ||||||
| setTimeout(() => { | ||||||
| window.location.href = url; | ||||||
| }, 0); | ||||||
| }, hangUrl); | ||||||
| }, | ||||||
| {timeout: 500}, | ||||||
| ); | ||||||
| const elapsed = Date.now() - start; | ||||||
|
|
||||||
| // The navigation started but timed out; the timeout error is logged and | ||||||
| // swallowed rather than thrown, and the pending navigation never | ||||||
| // committed, so the URL is unchanged and no navigatedToUrl is reported. | ||||||
| assert.strictEqual(result.navigatedToUrl, undefined); | ||||||
| assert.strictEqual(result.dialogHandled, false); | ||||||
| // Current behavior: the total wait is the 500ms navigation timeout plus | ||||||
| // the full 3s #stableDomTimeout, because the stable-DOM evaluation does | ||||||
| // not settle while the navigation is still pending (~3.5s in total). | ||||||
| // Assert an upper bound well below protocolTimeout-scale hangs. | ||||||
| assert.ok( | ||||||
| elapsed < 8_000, | ||||||
| `expected the wait to be bounded by the internal timeouts, took ${elapsed}ms`, | ||||||
| ); | ||||||
|
|
||||||
| // Note: no in-page cleanup here. Evaluations do not settle while the | ||||||
| // navigation is pending; the harness closes the page during teardown. | ||||||
| }); | ||||||
| }); | ||||||
|
|
||||||
| it('reports same-document hash navigations via the URL comparison', async () => { | ||||||
| await withMcpContext(async (response, context) => { | ||||||
| const mcpPage = context.getSelectedMcpPage(); | ||||||
| server.addHtmlRoute('/page.html', html`<p>content</p>`); | ||||||
| const pageUrl = server.getRoute('/page.html'); | ||||||
| await mcpPage.pptrPage.goto(pageUrl); | ||||||
|
|
||||||
| const result = await mcpPage.waitForEventsAfterAction(async () => { | ||||||
| await mcpPage.pptrPage.evaluate(() => { | ||||||
| window.location.hash = '#section'; | ||||||
| }); | ||||||
| }); | ||||||
|
|
||||||
| // Same-document navigations skip the full navigation wait but are still | ||||||
| // surfaced through the before/after URL comparison. | ||||||
|
Comment on lines
+171
to
+172
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| assert.strictEqual(result.navigatedToUrl, `${pageUrl}#section`); | ||||||
| assert.strictEqual(result.dialogHandled, false); | ||||||
| }); | ||||||
| }); | ||||||
|
|
||||||
| it('rethrows errors from the action', async () => { | ||||||
| await withMcpContext(async (response, context) => { | ||||||
| const mcpPage = context.getSelectedMcpPage(); | ||||||
| await mcpPage.pptrPage.setContent(html`<p>content</p>`); | ||||||
|
|
||||||
| await assert.rejects( | ||||||
| mcpPage.waitForEventsAfterAction(() => | ||||||
| Promise.reject(new Error('action failed')), | ||||||
| ), | ||||||
| /action failed/, | ||||||
| ); | ||||||
| }); | ||||||
| }); | ||||||
| }); | ||||||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.