chore: implement a test runner on top of mocha (#8866)

* chore: implement a test runner on top of mocha

This PR implements a test runner on top of mocha
that performs multiple mocha runs as defined in
TestSuites.json and compares the outcome of the runs
against TestExpectations.json. This allows us to
remove most of helpers from mocha-utils and be more
flexible when defining the test configurations.
This commit is contained in:
Alex Rudenko 2022-09-08 12:32:39 +02:00 committed by GitHub
parent f02b926245
commit d8830cbc55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 3537 additions and 1247 deletions

View File

@ -173,26 +173,12 @@ jobs:
run: npm run build:dev run: npm run build:dev
- name: Test types - name: Test types
run: npm run test:types run: npm run test:types
# On Linux we run all Chrome tests without retries and Firefox tests with retries. - name: Run all tests with xvfb
- name: Run all Chrome tests (only on Linux)
if: ${{ matrix.spec.name == 'Linux' }} if: ${{ matrix.spec.name == 'Linux' }}
run: xvfb-run --auto-servernum npm run test:chrome run: xvfb-run --auto-servernum npm run test
- name: Run all Firefox tests (only on Linux) - name: Run all tests without xvfb
if: ${{ matrix.spec.name == 'Linux' }}
uses: nick-invision/retry@v2
with:
command: xvfb-run --auto-servernum npm run test:firefox
timeout_minutes: 20
max_attempts: 3
# On other platforms we only run chrome:headless tests and continue on Firefox errors.
- name: Test Chrome Headless (not on Linux)
id: test-chrome
if: ${{ matrix.spec.name != 'Linux' }} if: ${{ matrix.spec.name != 'Linux' }}
run: npm run test:chrome:headless run: npm run test
- name: Run all Firefox tests (not on Linux)
if: ${{ matrix.spec.name != 'Linux' }}
continue-on-error: true
run: npm run test:firefox
- name: Test bundling and installation - name: Test bundling and installation
if: ${{ matrix.spec.name == 'Linux' }} if: ${{ matrix.spec.name == 'Linux' }}
run: | run: |

View File

@ -22,6 +22,6 @@ module.exports = {
exit: !!process.env.CI, exit: !!process.env.CI,
retries: process.env.CI ? 2 : 0, retries: process.env.CI ? 2 : 0,
parallel: !!process.env.PARALLEL, parallel: !!process.env.PARALLEL,
timeout: 25 * 1000, timeout: 25_000,
reporter: process.env.CI ? 'spec' : 'dot', reporter: process.env.CI ? 'spec' : 'dot',
}; };

18
package-lock.json generated
View File

@ -80,7 +80,8 @@
"text-diff": "1.0.1", "text-diff": "1.0.1",
"tsd": "0.22.0", "tsd": "0.22.0",
"tsx": "3.8.2", "tsx": "3.8.2",
"typescript": "4.7.4" "typescript": "4.7.4",
"zod": "3.18.0"
}, },
"engines": { "engines": {
"node": ">=14.1.0" "node": ">=14.1.0"
@ -7809,6 +7810,15 @@
"optionalDependencies": { "optionalDependencies": {
"commander": "^2.20.3" "commander": "^2.20.3"
} }
},
"node_modules/zod": {
"version": "3.18.0",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.18.0.tgz",
"integrity": "sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
} }
}, },
"dependencies": { "dependencies": {
@ -13501,6 +13511,12 @@
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
"validator": "^13.7.0" "validator": "^13.7.0"
} }
},
"zod": {
"version": "3.18.0",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.18.0.tgz",
"integrity": "sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA==",
"dev": true
} }
} }
} }

View File

@ -27,14 +27,14 @@
"node": ">=14.1.0" "node": ">=14.1.0"
}, },
"scripts": { "scripts": {
"test": "c8 --check-coverage --lines 93 run-s test:chrome:* test:firefox", "test": "cross-env MOZ_WEBRENDER=0 PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 c8 --check-coverage --lines 93 node utils/mochaRunner/lib/main.js",
"test:types": "tsd", "test:types": "tsd",
"test:install": "scripts/test-install.sh", "test:install": "scripts/test-install.sh",
"test:firefox": "cross-env PUPPETEER_PRODUCT=firefox MOZ_WEBRENDER=0 PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 mocha", "test:firefox": "npm run test -- --test-suite firefox-headless",
"test:chrome": "run-s test:chrome:*", "test:chrome": "run-s test:chrome:*",
"test:chrome:headless": "cross-env HEADLESS=true PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 mocha", "test:chrome:headless": "npm run test -- --test-suite chrome-headless",
"test:chrome:headless-chrome": "cross-env HEADLESS=chrome PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 mocha", "test:chrome:headless-chrome": "npm run test -- --test-suite chrome-new-headless",
"test:chrome:headful": "cross-env HEADLESS=false PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 mocha", "test:chrome:headful": "npm run test -- --test-suite chrome-headful",
"prepublishOnly": "npm run build", "prepublishOnly": "npm run build",
"prepare": "node typescript-if-required.js && husky install", "prepare": "node typescript-if-required.js && husky install",
"lint": "run-s lint:prettier lint:eslint", "lint": "run-s lint:prettier lint:eslint",
@ -139,6 +139,7 @@
"text-diff": "1.0.1", "text-diff": "1.0.1",
"tsd": "0.22.0", "tsd": "0.22.0",
"tsx": "3.8.2", "tsx": "3.8.2",
"typescript": "4.7.4" "typescript": "4.7.4",
"zod": "3.18.0"
} }
} }

1946
test/TestExpectations.json Normal file

File diff suppressed because it is too large Load Diff

41
test/TestSuites.json Normal file
View File

@ -0,0 +1,41 @@
{
"testSuites": [
{
"id": "chrome-headless",
"platforms": ["linux", "win32", "darwin"],
"parameters": ["chrome", "headless"]
},
{
"id": "chrome-headful",
"platforms": ["linux"],
"parameters": ["chrome", "headful"]
},
{
"id": "chrome-new-headless",
"platforms": ["linux"],
"parameters": ["chrome", "chrome-headless"]
},
{
"id": "firefox-headless",
"platforms": ["linux"],
"parameters": ["firefox", "headless"]
}
],
"parameterDefinitons": {
"chrome": {
"PUPPETEER_PRODUCT": "chrome"
},
"firefox": {
"PUPPETEER_PRODUCT": "firefox"
},
"headless": {
"HEADLESS": "true"
},
"headful": {
"HEADLESS": "false"
},
"chrome-headless": {
"HEADLESS": "chrome"
}
}
}

View File

@ -20,11 +20,11 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
describeChromeOnly,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js'; import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js';
import {it} from './mocha-utils.js';
describeChromeOnly('Target.createCDPSession', function () { describe('Target.createCDPSession', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();

View File

@ -17,6 +17,7 @@
import {EventEmitter} from '../../lib/cjs/puppeteer/common/EventEmitter.js'; import {EventEmitter} from '../../lib/cjs/puppeteer/common/EventEmitter.js';
import sinon from 'sinon'; import sinon from 'sinon';
import expect from 'expect'; import expect from 'expect';
import {it} from './mocha-utils.js';
describe('EventEmitter', () => { describe('EventEmitter', () => {
let emitter: EventEmitter; let emitter: EventEmitter;

View File

@ -14,8 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
import {describeChromeOnly} from './mocha-utils.js';
import expect from 'expect'; import expect from 'expect';
import { import {
NetworkManager, NetworkManager,
@ -25,12 +23,13 @@ import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {EventEmitter} from '../../lib/cjs/puppeteer/common/EventEmitter.js'; import {EventEmitter} from '../../lib/cjs/puppeteer/common/EventEmitter.js';
import {Frame} from '../../lib/cjs/puppeteer/common/Frame.js'; import {Frame} from '../../lib/cjs/puppeteer/common/Frame.js';
import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js'; import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js';
import {it} from './mocha-utils.js';
class MockCDPSession extends EventEmitter { class MockCDPSession extends EventEmitter {
async send(): Promise<any> {} async send(): Promise<any> {}
} }
describeChromeOnly('NetworkManager', () => { describe('NetworkManager', () => {
it('should process extra info on multiple redirects', async () => { it('should process extra info on multiple redirects', async () => {
const mockCDPSession = new MockCDPSession(); const mockCDPSession = new MockCDPSession();
new NetworkManager(mockCDPSession, true, { new NetworkManager(mockCDPSession, true, {

View File

@ -14,8 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
import {describeChromeOnly, getTestState} from './mocha-utils'; // eslint-disable-line import/extensions import {getTestState} from './mocha-utils'; // eslint-disable-line import/extensions
import utils from './utils.js'; import utils from './utils.js';
import {it} from './mocha-utils.js';
import expect from 'expect'; import expect from 'expect';
@ -24,7 +25,7 @@ import {
BrowserContext, BrowserContext,
} from '../../lib/cjs/puppeteer/common/Browser.js'; } from '../../lib/cjs/puppeteer/common/Browser.js';
describeChromeOnly('TargetManager', () => { describe('TargetManager', () => {
/* We use a special browser for this test as we need the --site-per-process flag */ /* We use a special browser for this test as we need the --site-per-process flag */
let browser: Browser; let browser: Browser;
let context: BrowserContext; let context: BrowserContext;

View File

@ -21,10 +21,10 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
describeFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describeFailsFirefox('Accessibility', function () { describe('Accessibility', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
@ -346,7 +346,7 @@ describeFailsFirefox('Accessibility', function () {
}); });
// Firefox does not support contenteditable="plaintext-only". // Firefox does not support contenteditable="plaintext-only".
describeFailsFirefox('plaintext contenteditable', function () { describe('plaintext contenteditable', function () {
it('plain text field with role should not have children', async () => { it('plain text field with role should not have children', async () => {
const {page} = getTestState(); const {page} = getTestState();

View File

@ -19,14 +19,14 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
describeChromeOnly,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {ElementHandle} from '../../lib/cjs/puppeteer/common/ElementHandle.js'; import {ElementHandle} from '../../lib/cjs/puppeteer/common/ElementHandle.js';
import utils from './utils.js'; import utils from './utils.js';
import assert from 'assert'; import assert from 'assert';
import {it} from './mocha-utils.js';
describeChromeOnly('AriaQueryHandler', () => { describe('AriaQueryHandler', () => {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();

View File

@ -16,6 +16,7 @@
import expect from 'expect'; import expect from 'expect';
import {getTestState, setupTestBrowserHooks} from './mocha-utils.js'; import {getTestState, setupTestBrowserHooks} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Browser specs', function () { describe('Browser specs', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();

View File

@ -15,12 +15,9 @@
*/ */
import expect from 'expect'; import expect from 'expect';
import { import {getTestState, setupTestBrowserHooks} from './mocha-utils.js';
getTestState,
itFailsFirefox,
setupTestBrowserHooks,
} from './mocha-utils.js';
import {waitEvent} from './utils.js'; import {waitEvent} from './utils.js';
import {it} from './mocha-utils.js';
describe('BrowserContext', function () { describe('BrowserContext', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -60,7 +57,7 @@ describe('BrowserContext', function () {
await context.close(); await context.close();
expect((await browser.pages()).length).toBe(1); expect((await browser.pages()).length).toBe(1);
}); });
itFailsFirefox('window.open should use parent tab context', async () => { it('window.open should use parent tab context', async () => {
const {browser, server} = getTestState(); const {browser, server} = getTestState();
const context = await browser.createIncognitoBrowserContext(); const context = await browser.createIncognitoBrowserContext();
@ -75,7 +72,7 @@ describe('BrowserContext', function () {
expect(popupTarget.browserContext()).toBe(context); expect(popupTarget.browserContext()).toBe(context);
await context.close(); await context.close();
}); });
itFailsFirefox('should fire target events', async () => { it('should fire target events', async () => {
const {browser, server} = getTestState(); const {browser, server} = getTestState();
const context = await browser.createIncognitoBrowserContext(); const context = await browser.createIncognitoBrowserContext();
@ -99,7 +96,7 @@ describe('BrowserContext', function () {
]); ]);
await context.close(); await context.close();
}); });
itFailsFirefox('should wait for a target', async () => { it('should wait for a target', async () => {
const {browser, puppeteer, server} = getTestState(); const {browser, puppeteer, server} = getTestState();
const context = await browser.createIncognitoBrowserContext(); const context = await browser.createIncognitoBrowserContext();
@ -156,7 +153,7 @@ describe('BrowserContext', function () {
await context.close(); await context.close();
}); });
itFailsFirefox('should isolate localStorage and cookies', async () => { it('should isolate localStorage and cookies', async () => {
const {browser, server} = getTestState(); const {browser, server} = getTestState();
// Create two incognito contexts. // Create two incognito contexts.
@ -216,7 +213,7 @@ describe('BrowserContext', function () {
expect(browser.browserContexts().length).toBe(1); expect(browser.browserContexts().length).toBe(1);
}); });
itFailsFirefox('should work across sessions', async () => { it('should work across sessions', async () => {
const {browser, puppeteer} = getTestState(); const {browser, puppeteer} = getTestState();
expect(browser.browserContexts().length).toBe(1); expect(browser.browserContexts().length).toBe(1);

View File

@ -19,10 +19,10 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
describeChromeOnly,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describeChromeOnly('Chromium-Specific Launcher tests', function () { describe('Chromium-Specific Launcher tests', function () {
describe('Puppeteer.launch |browserURL| option', function () { describe('Puppeteer.launch |browserURL| option', function () {
it('should be able to connect using browserUrl, with and without trailing slash', async () => { it('should be able to connect using browserUrl, with and without trailing slash', async () => {
const {defaultBrowserOptions, puppeteer} = getTestState(); const {defaultBrowserOptions, puppeteer} = getTestState();
@ -138,7 +138,7 @@ describeChromeOnly('Chromium-Specific Launcher tests', function () {
}); });
}); });
describeChromeOnly('Chromium-Specific Page Tests', function () { describe('Chromium-Specific Page Tests', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
it('Page.setRequestInterception should work with intervention headers', async () => { it('Page.setRequestInterception should work with intervention headers', async () => {

View File

@ -19,9 +19,9 @@ import {
getTestState, getTestState,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
setupTestBrowserHooks, setupTestBrowserHooks,
itFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import utils from './utils.js'; import utils from './utils.js';
import {it} from './mocha-utils.js';
describe('Page.click', function () { describe('Page.click', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -52,24 +52,21 @@ describe('Page.click', function () {
}) })
).toBe(42); ).toBe(42);
}); });
itFailsFirefox( it('should click the button if window.Node is removed', async () => {
'should click the button if window.Node is removed', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/button.html'); await page.goto(server.PREFIX + '/input/button.html');
await page.evaluate(() => {
// @ts-expect-error Expected.
return delete window.Node;
});
await page.click('button');
expect(
await page.evaluate(() => { await page.evaluate(() => {
// @ts-expect-error Expected. return (globalThis as any).result;
return delete window.Node; })
}); ).toBe('Clicked');
await page.click('button'); });
expect(
await page.evaluate(() => {
return (globalThis as any).result;
})
).toBe('Clicked');
}
);
// @see https://github.com/puppeteer/puppeteer/issues/4281 // @see https://github.com/puppeteer/puppeteer/issues/4281
it('should click on a span with an inline element inside', async () => { it('should click on a span with an inline element inside', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -110,7 +107,7 @@ describe('Page.click', function () {
}) })
).toBe('Clicked'); ).toBe('Clicked');
}); });
itFailsFirefox('should click with disabled javascript', async () => { it('should click with disabled javascript', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setJavaScriptEnabled(false); await page.setJavaScriptEnabled(false);
@ -240,7 +237,7 @@ describe('Page.click', function () {
).toBe(false); ).toBe(false);
}); });
itFailsFirefox('should click on checkbox label and toggle', async () => { it('should click on checkbox label and toggle', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/checkbox.html'); await page.goto(server.PREFIX + '/input/checkbox.html');

View File

@ -19,8 +19,8 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
itFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Cookie specs', () => { describe('Cookie specs', () => {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -128,7 +128,7 @@ describe('Cookie specs', () => {
}, },
]); ]);
}); });
itFailsFirefox('should get cookies from multiple urls', async () => { it('should get cookies from multiple urls', async () => {
const {page} = getTestState(); const {page} = getTestState();
await page.setCookie( await page.setCookie(
{ {
@ -184,7 +184,7 @@ describe('Cookie specs', () => {
}); });
}); });
describe('Page.setCookie', function () { describe('Page.setCookie', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -198,7 +198,7 @@ describe('Cookie specs', () => {
}) })
).toEqual('password=123456'); ).toEqual('password=123456');
}); });
itFailsFirefox('should isolate cookies in browser contexts', async () => { it('should isolate cookies in browser contexts', async () => {
const {page, server, browser} = getTestState(); const {page, server, browser} = getTestState();
const anotherContext = await browser.createIncognitoBrowserContext(); const anotherContext = await browser.createIncognitoBrowserContext();
@ -220,7 +220,7 @@ describe('Cookie specs', () => {
expect(cookies2[0]!.value).toBe('page2value'); expect(cookies2[0]!.value).toBe('page2value');
await anotherContext.close(); await anotherContext.close();
}); });
itFailsFirefox('should set multiple cookies', async () => { it('should set multiple cookies', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -257,7 +257,7 @@ describe('Cookie specs', () => {
expect(cookies[0]!.session).toBe(true); expect(cookies[0]!.session).toBe(true);
expect(cookies[0]!.expires).toBe(-1); expect(cookies[0]!.expires).toBe(-1);
}); });
itFailsFirefox('should set cookie with reasonable defaults', async () => { it('should set cookie with reasonable defaults', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -288,7 +288,7 @@ describe('Cookie specs', () => {
] ]
); );
}); });
itFailsFirefox('should set a cookie with a path', async () => { it('should set a cookie with a path', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
@ -365,22 +365,19 @@ describe('Cookie specs', () => {
'At least one of the url and domain needs to be specified' 'At least one of the url and domain needs to be specified'
); );
}); });
itFailsFirefox( it('should default to setting secure cookie for HTTPS websites', async () => {
'should default to setting secure cookie for HTTPS websites', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const SECURE_URL = 'https://example.com'; const SECURE_URL = 'https://example.com';
await page.setCookie({ await page.setCookie({
url: SECURE_URL, url: SECURE_URL,
name: 'foo', name: 'foo',
value: 'bar', value: 'bar',
}); });
const [cookie] = await page.cookies(SECURE_URL); const [cookie] = await page.cookies(SECURE_URL);
expect(cookie!.secure).toBe(true); expect(cookie!.secure).toBe(true);
} });
);
it('should be able to set unsecure cookie for HTTP website', async () => { it('should be able to set unsecure cookie for HTTP website', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -394,7 +391,7 @@ describe('Cookie specs', () => {
const [cookie] = await page.cookies(HTTP_URL); const [cookie] = await page.cookies(HTTP_URL);
expect(cookie!.secure).toBe(false); expect(cookie!.secure).toBe(false);
}); });
itFailsFirefox('should set a cookie on a different domain', async () => { it('should set a cookie on a different domain', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -422,7 +419,7 @@ describe('Cookie specs', () => {
}, },
]); ]);
}); });
itFailsFirefox('should set cookies from a frame', async () => { it('should set cookies from a frame', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
@ -482,71 +479,68 @@ describe('Cookie specs', () => {
}, },
]); ]);
}); });
itFailsFirefox( it('should set secure same-site cookies from a frame', async () => {
'should set secure same-site cookies from a frame', const {httpsServer, puppeteer, defaultBrowserOptions} = getTestState();
async () => {
const {httpsServer, puppeteer, defaultBrowserOptions} = getTestState();
const browser = await puppeteer.launch({ const browser = await puppeteer.launch({
...defaultBrowserOptions, ...defaultBrowserOptions,
ignoreHTTPSErrors: true, ignoreHTTPSErrors: true,
});
const page = await browser.newPage();
try {
await page.goto(httpsServer.PREFIX + '/grid.html');
await page.evaluate(src => {
let fulfill!: () => void;
const promise = new Promise<void>(x => {
return (fulfill = x);
});
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.onload = fulfill;
iframe.src = src;
return promise;
}, httpsServer.CROSS_PROCESS_PREFIX);
await page.setCookie({
name: '127-same-site-cookie',
value: 'best',
url: httpsServer.CROSS_PROCESS_PREFIX,
sameSite: 'None',
}); });
const page = await browser.newPage(); expect(await page.frames()[1]!.evaluate('document.cookie')).toBe(
'127-same-site-cookie=best'
try { );
await page.goto(httpsServer.PREFIX + '/grid.html'); expectCookieEquals(
await page.evaluate(src => { await page.cookies(httpsServer.CROSS_PROCESS_PREFIX),
let fulfill!: () => void; [
const promise = new Promise<void>(x => { {
return (fulfill = x); name: '127-same-site-cookie',
}); value: 'best',
const iframe = document.createElement('iframe'); domain: '127.0.0.1',
document.body.appendChild(iframe); path: '/',
iframe.onload = fulfill; sameParty: false,
iframe.src = src; expires: -1,
return promise; size: 24,
}, httpsServer.CROSS_PROCESS_PREFIX); httpOnly: false,
await page.setCookie({ sameSite: 'None',
name: '127-same-site-cookie', secure: true,
value: 'best', session: true,
url: httpsServer.CROSS_PROCESS_PREFIX, sourcePort: 443,
sameSite: 'None', sourceScheme: 'Secure',
}); },
]
expect(await page.frames()[1]!.evaluate('document.cookie')).toBe( );
'127-same-site-cookie=best' } finally {
); await page.close();
expectCookieEquals( await browser.close();
await page.cookies(httpsServer.CROSS_PROCESS_PREFIX),
[
{
name: '127-same-site-cookie',
value: 'best',
domain: '127.0.0.1',
path: '/',
sameParty: false,
expires: -1,
size: 24,
httpOnly: false,
sameSite: 'None',
secure: true,
session: true,
sourcePort: 443,
sourceScheme: 'Secure',
},
]
);
} finally {
await page.close();
await browser.close();
}
} }
); });
}); });
describe('Page.deleteCookie', function () { describe('Page.deleteCookie', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);

View File

@ -19,11 +19,11 @@ import {
getTestState, getTestState,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
setupTestBrowserHooks, setupTestBrowserHooks,
describeChromeOnly,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Coverage specs', function () { describe('Coverage specs', function () {
describeChromeOnly('JSCoverage', function () { describe('JSCoverage', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
@ -202,7 +202,7 @@ describe('Coverage specs', function () {
}); });
}); });
describeChromeOnly('CSSCoverage', function () { describe('CSSCoverage', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();

View File

@ -19,8 +19,8 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
itFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('DefaultBrowserContext', function () { describe('DefaultBrowserContext', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -48,7 +48,7 @@ describe('DefaultBrowserContext', function () {
}, },
]); ]);
}); });
itFailsFirefox('page.setCookie() should work', async () => { it('page.setCookie() should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -78,7 +78,7 @@ describe('DefaultBrowserContext', function () {
}, },
]); ]);
}); });
itFailsFirefox('page.deleteCookie() should work', async () => { it('page.deleteCookie() should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);

View File

@ -20,8 +20,8 @@ import {
getTestState, getTestState,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
setupTestBrowserHooks, setupTestBrowserHooks,
itFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Page.Events.Dialog', function () { describe('Page.Events.Dialog', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -46,7 +46,7 @@ describe('Page.Events.Dialog', function () {
expect(dialog.message()).toBe('yo'); expect(dialog.message()).toBe('yo');
}); });
itFailsFirefox('should allow accepting prompts', async () => { it('should allow accepting prompts', async () => {
const {page} = getTestState(); const {page} = getTestState();
const onDialog = sinon.stub().callsFake(dialog => { const onDialog = sinon.stub().callsFake(dialog => {

View File

@ -19,10 +19,10 @@ import {
getTestState, getTestState,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
setupTestBrowserHooks, setupTestBrowserHooks,
describeChromeOnly,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describeChromeOnly('Input.drag', function () { describe('Input.drag', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
it('should throw an exception if not enabled before usage', async () => { it('should throw an exception if not enabled before usage', async () => {

View File

@ -18,12 +18,11 @@ import expect from 'expect';
import sinon from 'sinon'; import sinon from 'sinon';
import {ElementHandle} from '../../lib/cjs/puppeteer/common/ElementHandle.js'; import {ElementHandle} from '../../lib/cjs/puppeteer/common/ElementHandle.js';
import { import {
describeFailsFirefox,
getTestState, getTestState,
itFailsFirefox,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
import utils from './utils.js'; import utils from './utils.js';
@ -31,7 +30,7 @@ describe('ElementHandle specs', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
describeFailsFirefox('ElementHandle.boundingBox', function () { describe('ElementHandle.boundingBox', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -96,7 +95,7 @@ describe('ElementHandle specs', function () {
}); });
}); });
describeFailsFirefox('ElementHandle.boxModel', function () { describe('ElementHandle.boxModel', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -162,7 +161,7 @@ describe('ElementHandle specs', function () {
}); });
describe('ElementHandle.contentFrame', function () { describe('ElementHandle.contentFrame', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -175,7 +174,7 @@ describe('ElementHandle specs', function () {
describe('ElementHandle.click', function () { describe('ElementHandle.click', function () {
// See https://github.com/puppeteer/puppeteer/issues/7175 // See https://github.com/puppeteer/puppeteer/issues/7175
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/button.html'); await page.goto(server.PREFIX + '/input/button.html');

View File

@ -20,9 +20,8 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
itFailsFirefox,
describeFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Emulation', () => { describe('Emulation', () => {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -132,7 +131,7 @@ describe('Emulation', () => {
}) })
).toBe(true); ).toBe(true);
}); });
itFailsFirefox('should support landscape emulation', async () => { it('should support landscape emulation', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/mobile.html'); await page.goto(server.PREFIX + '/mobile.html');
@ -173,7 +172,7 @@ describe('Emulation', () => {
}) })
).toContain('iPhone'); ).toContain('iPhone');
}); });
itFailsFirefox('should support clicking', async () => { it('should support clicking', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.emulate(iPhone); await page.emulate(iPhone);
@ -192,7 +191,7 @@ describe('Emulation', () => {
}); });
describe('Page.emulateMediaType', function () { describe('Page.emulateMediaType', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
expect( expect(
@ -240,7 +239,7 @@ describe('Emulation', () => {
}); });
describe('Page.emulateMediaFeatures', function () { describe('Page.emulateMediaFeatures', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
await page.emulateMediaFeatures([ await page.emulateMediaFeatures([
@ -370,7 +369,7 @@ describe('Emulation', () => {
}); });
}); });
describeFailsFirefox('Page.emulateTimezone', function () { describe('Page.emulateTimezone', function () {
it('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -425,7 +424,7 @@ describe('Emulation', () => {
}); });
}); });
describeFailsFirefox('Page.emulateVisionDeficiency', function () { describe('Page.emulateVisionDeficiency', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -489,7 +488,7 @@ describe('Emulation', () => {
}); });
}); });
describeFailsFirefox('Page.emulateNetworkConditions', function () { describe('Page.emulateNetworkConditions', function () {
it('should change navigator.connection.effectiveType', async () => { it('should change navigator.connection.effectiveType', async () => {
const {page, puppeteer} = getTestState(); const {page, puppeteer} = getTestState();
@ -511,7 +510,7 @@ describe('Emulation', () => {
}); });
}); });
describeFailsFirefox('Page.emulateCPUThrottling', function () { describe('Page.emulateCPUThrottling', function () {
it('should change the CPU throttling rate successfully', async () => { it('should change the CPU throttling rate successfully', async () => {
const {page} = getTestState(); const {page} = getTestState();

View File

@ -20,9 +20,8 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
itFailsFirefox,
describeFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
const bigint = typeof BigInt !== 'undefined'; const bigint = typeof BigInt !== 'undefined';
@ -115,18 +114,15 @@ describe('Evaluation specs', function () {
await page.goto(server.PREFIX + '/global-var.html'); await page.goto(server.PREFIX + '/global-var.html');
expect(await page.evaluate('globalVar')).toBe(123); expect(await page.evaluate('globalVar')).toBe(123);
}); });
itFailsFirefox( it('should return undefined for objects with symbols', async () => {
'should return undefined for objects with symbols', const {page} = getTestState();
async () => {
const {page} = getTestState();
expect( expect(
await page.evaluate(() => { await page.evaluate(() => {
return [Symbol('foo4')]; return [Symbol('foo4')];
}) })
).toBe(undefined); ).toBe(undefined);
} });
);
it('should work with function shorthands', async () => { it('should work with function shorthands', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -155,7 +151,7 @@ describe('Evaluation specs', function () {
); );
expect(result).toBe(42); expect(result).toBe(42);
}); });
itFailsFirefox('should throw when evaluation triggers reload', async () => { it('should throw when evaluation triggers reload', async () => {
const {page} = getTestState(); const {page} = getTestState();
let error!: Error; let error!: Error;
@ -189,7 +185,7 @@ describe('Evaluation specs', function () {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(await frameEvaluation).toBe(42); expect(await frameEvaluation).toBe(42);
}); });
itFailsFirefox('should work from-inside an exposed function', async () => { it('should work from-inside an exposed function', async () => {
const {page} = getTestState(); const {page} = getTestState();
// Setup inpage callback, which calls Page.evaluate // Setup inpage callback, which calls Page.evaluate
@ -324,19 +320,16 @@ describe('Evaluation specs', function () {
}) })
).toEqual({}); ).toEqual({});
}); });
itFailsFirefox( it('should return undefined for non-serializable objects', async () => {
'should return undefined for non-serializable objects', const {page} = getTestState();
async () => {
const {page} = getTestState();
expect( expect(
await page.evaluate(() => { await page.evaluate(() => {
return window; return window;
}) })
).toBe(undefined); ).toBe(undefined);
} });
); it('should fail for circular object', async () => {
itFailsFirefox('should fail for circular object', async () => {
const {page} = getTestState(); const {page} = getTestState();
const result = await page.evaluate(() => { const result = await page.evaluate(() => {
@ -347,7 +340,7 @@ describe('Evaluation specs', function () {
}); });
expect(result).toBe(undefined); expect(result).toBe(undefined);
}); });
itFailsFirefox('should be able to throw a tricky error', async () => { it('should be able to throw a tricky error', async () => {
const {page} = getTestState(); const {page} = getTestState();
const windowHandle = await page.evaluateHandle(() => { const windowHandle = await page.evaluateHandle(() => {
@ -410,28 +403,25 @@ describe('Evaluation specs', function () {
}); });
expect(error.message).toContain('JSHandle is disposed'); expect(error.message).toContain('JSHandle is disposed');
}); });
itFailsFirefox( it('should throw if elementHandles are from other frames', async () => {
'should throw if elementHandles are from other frames', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const bodyHandle = await page.frames()[1]!.$('body'); const bodyHandle = await page.frames()[1]!.$('body');
let error!: Error; let error!: Error;
await page await page
.evaluate(body => { .evaluate(body => {
return body?.innerHTML; return body?.innerHTML;
}, bodyHandle) }, bodyHandle)
.catch(error_ => { .catch(error_ => {
return (error = error_); return (error = error_);
}); });
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain( expect(error.message).toContain(
'JSHandles can be evaluated only in the context they were created' 'JSHandles can be evaluated only in the context they were created'
); );
} });
); it('should simulate a user gesture', async () => {
itFailsFirefox('should simulate a user gesture', async () => {
const {page} = getTestState(); const {page} = getTestState();
const result = await page.evaluate(() => { const result = await page.evaluate(() => {
@ -441,7 +431,7 @@ describe('Evaluation specs', function () {
}); });
expect(result).toBe(true); expect(result).toBe(true);
}); });
itFailsFirefox('should throw a nice error after a navigation', async () => { it('should throw a nice error after a navigation', async () => {
const {page} = getTestState(); const {page} = getTestState();
const executionContext = await page.mainFrame().executionContext(); const executionContext = await page.mainFrame().executionContext();
@ -461,19 +451,16 @@ describe('Evaluation specs', function () {
}); });
expect((error as Error).message).toContain('navigation'); expect((error as Error).message).toContain('navigation');
}); });
itFailsFirefox( it('should not throw an error when evaluation does a navigation', async () => {
'should not throw an error when evaluation does a navigation', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.PREFIX + '/one-style.html'); await page.goto(server.PREFIX + '/one-style.html');
const result = await page.evaluate(() => { const result = await page.evaluate(() => {
(window as any).location = '/empty.html'; (window as any).location = '/empty.html';
return [42]; return [42];
}); });
expect(result).toEqual([42]); expect(result).toEqual([42]);
} });
);
it('should transfer 100Mb of data from page to node.js', async function () { it('should transfer 100Mb of data from page to node.js', async function () {
const {page} = getTestState(); const {page} = getTestState();
@ -499,7 +486,7 @@ describe('Evaluation specs', function () {
}); });
}); });
describeFailsFirefox('Page.evaluateOnNewDocument', function () { describe('Page.evaluateOnNewDocument', function () {
it('should evaluate before anything else on the page', async () => { it('should evaluate before anything else on the page', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();

View File

@ -17,12 +17,13 @@
/* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-var-requires */
import expect from 'expect'; import expect from 'expect';
import {getTestState, itHeadlessOnly} from './mocha-utils.js'; import {getTestState} from './mocha-utils.js';
import path from 'path'; import path from 'path';
import {it} from './mocha-utils.js';
describe('Fixtures', function () { describe('Fixtures', function () {
itHeadlessOnly('dumpio option should work with pipe option', async () => { it('dumpio option should work with pipe option', async () => {
const {defaultBrowserOptions, puppeteerPath, headless} = getTestState(); const {defaultBrowserOptions, puppeteerPath, headless} = getTestState();
if (headless === 'chrome') { if (headless === 'chrome') {
// This test only works in the old headless mode. // This test only works in the old headless mode.

View File

@ -19,18 +19,18 @@ import {CDPSession} from '../../lib/cjs/puppeteer/common/Connection.js';
import {Frame} from '../../lib/cjs/puppeteer/common/Frame.js'; import {Frame} from '../../lib/cjs/puppeteer/common/Frame.js';
import { import {
getTestState, getTestState,
itFailsFirefox,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
import utils, {dumpFrames} from './utils.js'; import utils, {dumpFrames} from './utils.js';
import {it} from './mocha-utils.js';
describe('Frame specs', function () { describe('Frame specs', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
describe('Frame.executionContext', function () { describe('Frame.executionContext', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -80,7 +80,7 @@ describe('Frame specs', function () {
}); });
describe('Frame.evaluate', function () { describe('Frame.evaluate', function () {
itFailsFirefox('should throw for detached frames', async () => { it('should throw for detached frames', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
const frame1 = (await utils.attachFrame( const frame1 = (await utils.attachFrame(
@ -126,7 +126,7 @@ describe('Frame specs', function () {
}); });
describe('Frame Management', function () { describe('Frame Management', function () {
itFailsFirefox('should handle nested frames', async () => { it('should handle nested frames', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/frames/nested-frames.html'); await page.goto(server.PREFIX + '/frames/nested-frames.html');
@ -138,40 +138,37 @@ describe('Frame specs', function () {
' http://localhost:<PORT>/frames/frame.html (aframe)', ' http://localhost:<PORT>/frames/frame.html (aframe)',
]); ]);
}); });
itFailsFirefox( it('should send events when frames are manipulated dynamically', async () => {
'should send events when frames are manipulated dynamically', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
// validate frameattached events // validate frameattached events
const attachedFrames: Frame[] = []; const attachedFrames: Frame[] = [];
page.on('frameattached', frame => { page.on('frameattached', frame => {
return attachedFrames.push(frame); return attachedFrames.push(frame);
}); });
await utils.attachFrame(page, 'frame1', './assets/frame.html'); await utils.attachFrame(page, 'frame1', './assets/frame.html');
expect(attachedFrames.length).toBe(1); expect(attachedFrames.length).toBe(1);
expect(attachedFrames[0]!.url()).toContain('/assets/frame.html'); expect(attachedFrames[0]!.url()).toContain('/assets/frame.html');
// validate framenavigated events // validate framenavigated events
const navigatedFrames: Frame[] = []; const navigatedFrames: Frame[] = [];
page.on('framenavigated', frame => { page.on('framenavigated', frame => {
return navigatedFrames.push(frame); return navigatedFrames.push(frame);
}); });
await utils.navigateFrame(page, 'frame1', './empty.html'); await utils.navigateFrame(page, 'frame1', './empty.html');
expect(navigatedFrames.length).toBe(1); expect(navigatedFrames.length).toBe(1);
expect(navigatedFrames[0]!.url()).toBe(server.EMPTY_PAGE); expect(navigatedFrames[0]!.url()).toBe(server.EMPTY_PAGE);
// validate framedetached events // validate framedetached events
const detachedFrames: Frame[] = []; const detachedFrames: Frame[] = [];
page.on('framedetached', frame => { page.on('framedetached', frame => {
return detachedFrames.push(frame); return detachedFrames.push(frame);
}); });
await utils.detachFrame(page, 'frame1'); await utils.detachFrame(page, 'frame1');
expect(detachedFrames.length).toBe(1); expect(detachedFrames.length).toBe(1);
expect(detachedFrames[0]!.isDetached()).toBe(true); expect(detachedFrames[0]!.isDetached()).toBe(true);
} });
);
it('should send "framenavigated" when navigating on anchor URLs', async () => { it('should send "framenavigated" when navigating on anchor URLs', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -259,7 +256,7 @@ describe('Frame specs', function () {
expect(detachedFrames.length).toBe(4); expect(detachedFrames.length).toBe(4);
expect(navigatedFrames.length).toBe(1); expect(navigatedFrames.length).toBe(1);
}); });
itFailsFirefox('should report frame from-inside shadow DOM', async () => { it('should report frame from-inside shadow DOM', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/shadow.html'); await page.goto(server.PREFIX + '/shadow.html');
@ -274,7 +271,7 @@ describe('Frame specs', function () {
expect(page.frames().length).toBe(2); expect(page.frames().length).toBe(2);
expect(page.frames()[1]!.url()).toBe(server.EMPTY_PAGE); expect(page.frames()[1]!.url()).toBe(server.EMPTY_PAGE);
}); });
itFailsFirefox('should report frame.name()', async () => { it('should report frame.name()', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE); await utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE);
@ -291,7 +288,7 @@ describe('Frame specs', function () {
expect(page.frames()[1]!.name()).toBe('theFrameId'); expect(page.frames()[1]!.name()).toBe('theFrameId');
expect(page.frames()[2]!.name()).toBe('theFrameName'); expect(page.frames()[2]!.name()).toBe('theFrameName');
}); });
itFailsFirefox('should report frame.parent()', async () => { it('should report frame.parent()', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
@ -300,31 +297,24 @@ describe('Frame specs', function () {
expect(page.frames()[1]!.parentFrame()).toBe(page.mainFrame()); expect(page.frames()[1]!.parentFrame()).toBe(page.mainFrame());
expect(page.frames()[2]!.parentFrame()).toBe(page.mainFrame()); expect(page.frames()[2]!.parentFrame()).toBe(page.mainFrame());
}); });
itFailsFirefox( it('should report different frame instance when frame re-attaches', async () => {
'should report different frame instance when frame re-attaches', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
const frame1 = await utils.attachFrame( const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
page, await page.evaluate(() => {
'frame1', (globalThis as any).frame = document.querySelector('#frame1');
server.EMPTY_PAGE (globalThis as any).frame.remove();
); });
await page.evaluate(() => { expect(frame1!.isDetached()).toBe(true);
(globalThis as any).frame = document.querySelector('#frame1'); const [frame2] = await Promise.all([
(globalThis as any).frame.remove(); utils.waitEvent(page, 'frameattached'),
}); page.evaluate(() => {
expect(frame1!.isDetached()).toBe(true); return document.body.appendChild((globalThis as any).frame);
const [frame2] = await Promise.all([ }),
utils.waitEvent(page, 'frameattached'), ]);
page.evaluate(() => { expect(frame2.isDetached()).toBe(false);
return document.body.appendChild((globalThis as any).frame); expect(frame1).not.toBe(frame2);
}), });
]);
expect(frame2.isDetached()).toBe(false);
expect(frame1).not.toBe(frame2);
}
);
it('should support url fragment', async () => { it('should support url fragment', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -335,7 +325,7 @@ describe('Frame specs', function () {
server.PREFIX + '/frames/frame.html?param=value#fragment' server.PREFIX + '/frames/frame.html?param=value#fragment'
); );
}); });
itFailsFirefox('should support lazy frames', async () => { it('should support lazy frames', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 1000, height: 1000}); await page.setViewport({width: 1000, height: 1000});

View File

@ -24,11 +24,8 @@ import {
PuppeteerLaunchOptions, PuppeteerLaunchOptions,
PuppeteerNode, PuppeteerNode,
} from '../../lib/cjs/puppeteer/node/Puppeteer.js'; } from '../../lib/cjs/puppeteer/node/Puppeteer.js';
import { import {getTestState} from './mocha-utils.js';
describeChromeOnly, import {it} from './mocha-utils.js';
getTestState,
itFailsWindows,
} from './mocha-utils.js';
const rmAsync = promisify(rimraf); const rmAsync = promisify(rimraf);
const mkdtempAsync = promisify(fs.mkdtemp); const mkdtempAsync = promisify(fs.mkdtemp);
@ -44,7 +41,7 @@ const serviceWorkerExtensionPath = path.join(
'extension' 'extension'
); );
describeChromeOnly('headful tests', function () { describe('headful tests', function () {
/* These tests fire up an actual browser so let's /* These tests fire up an actual browser so let's
* allow a higher timeout * allow a higher timeout
*/ */
@ -214,41 +211,38 @@ describeChromeOnly('headful tests', function () {
expect(pages).toEqual(['about:blank']); expect(pages).toEqual(['about:blank']);
await browser.close(); await browser.close();
}); });
itFailsWindows( it('headless should be able to read cookies written by headful', async () => {
'headless should be able to read cookies written by headful', /* Needs investigation into why but this fails consistently on Windows CI. */
async () => { const {server, puppeteer} = getTestState();
/* Needs investigation into why but this fails consistently on Windows CI. */
const {server, puppeteer} = getTestState();
const userDataDir = await mkdtempAsync(TMP_FOLDER); const userDataDir = await mkdtempAsync(TMP_FOLDER);
// Write a cookie in headful chrome // Write a cookie in headful chrome
const headfulBrowser = await launchBrowser( const headfulBrowser = await launchBrowser(
puppeteer, puppeteer,
Object.assign({userDataDir}, headfulOptions) Object.assign({userDataDir}, headfulOptions)
); );
const headfulPage = await headfulBrowser.newPage(); const headfulPage = await headfulBrowser.newPage();
await headfulPage.goto(server.EMPTY_PAGE); await headfulPage.goto(server.EMPTY_PAGE);
await headfulPage.evaluate(() => { await headfulPage.evaluate(() => {
return (document.cookie = return (document.cookie =
'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT'); 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
}); });
await headfulBrowser.close(); await headfulBrowser.close();
// Read the cookie from headless chrome // Read the cookie from headless chrome
const headlessBrowser = await launchBrowser( const headlessBrowser = await launchBrowser(
puppeteer, puppeteer,
Object.assign({userDataDir}, headlessOptions) Object.assign({userDataDir}, headlessOptions)
); );
const headlessPage = await headlessBrowser.newPage(); const headlessPage = await headlessBrowser.newPage();
await headlessPage.goto(server.EMPTY_PAGE); await headlessPage.goto(server.EMPTY_PAGE);
const cookie = await headlessPage.evaluate(() => { const cookie = await headlessPage.evaluate(() => {
return document.cookie; return document.cookie;
}); });
await headlessBrowser.close(); await headlessBrowser.close();
// This might throw. See https://github.com/puppeteer/puppeteer/issues/2778 // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778
await rmAsync(userDataDir).catch(() => {}); await rmAsync(userDataDir).catch(() => {});
expect(cookie).toBe('foo=true'); expect(cookie).toBe('foo=true');
} });
);
// TODO: Support OOOPIF. @see https://github.com/puppeteer/puppeteer/issues/2548 // TODO: Support OOOPIF. @see https://github.com/puppeteer/puppeteer/issues/2548
xit('OOPIF: should report google.com frame', async () => { xit('OOPIF: should report google.com frame', async () => {
const {server, puppeteer} = getTestState(); const {server, puppeteer} = getTestState();

View File

@ -20,10 +20,10 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
describeFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describeFailsFirefox('Emulate idle state', () => { describe('Emulate idle state', () => {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();

View File

@ -22,11 +22,8 @@ import {
} from '../../lib/cjs/puppeteer/common/Browser.js'; } from '../../lib/cjs/puppeteer/common/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js'; import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js'; import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js';
import { import {getTestState} from './mocha-utils.js';
getTestState, import {it} from './mocha-utils.js';
describeFailsFirefox,
itFailsFirefox,
} from './mocha-utils.js';
describe('ignoreHTTPSErrors', function () { describe('ignoreHTTPSErrors', function () {
/* Note that this test creates its own browser rather than use /* Note that this test creates its own browser rather than use
@ -59,7 +56,7 @@ describe('ignoreHTTPSErrors', function () {
await context.close(); await context.close();
}); });
describeFailsFirefox('Response.securityDetails', function () { describe('Response.securityDetails', function () {
it('should work', async () => { it('should work', async () => {
const {httpsServer} = getTestState(); const {httpsServer} = getTestState();
@ -119,7 +116,7 @@ describe('ignoreHTTPSErrors', function () {
expect(error).toBeUndefined(); expect(error).toBeUndefined();
expect(response.ok()).toBe(true); expect(response.ok()).toBe(true);
}); });
itFailsFirefox('should work with request interception', async () => { it('should work with request interception', async () => {
const {httpsServer} = getTestState(); const {httpsServer} = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
@ -129,7 +126,7 @@ describe('ignoreHTTPSErrors', function () {
const response = (await page.goto(httpsServer.EMPTY_PAGE))!; const response = (await page.goto(httpsServer.EMPTY_PAGE))!;
expect(response.status()).toBe(200); expect(response.status()).toBe(200);
}); });
itFailsFirefox('should work with mixed content', async () => { it('should work with mixed content', async () => {
const {server, httpsServer} = getTestState(); const {server, httpsServer} = getTestState();
httpsServer.setRoute('/mixedcontent.html', (_req, res) => { httpsServer.setRoute('/mixedcontent.html', (_req, res) => {

View File

@ -20,8 +20,8 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
describeFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
const FILE_TO_UPLOAD = path.join(__dirname, '/../assets/file-to-upload.txt'); const FILE_TO_UPLOAD = path.join(__dirname, '/../assets/file-to-upload.txt');
@ -29,7 +29,7 @@ describe('input tests', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
describeFailsFirefox('input', function () { describe('input', function () {
it('should upload the file', async () => { it('should upload the file', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -76,7 +76,7 @@ describe('input tests', function () {
}); });
}); });
describeFailsFirefox('Page.waitForFileChooser', function () { describe('Page.waitForFileChooser', function () {
it('should work when file input is attached to DOM', async () => { it('should work when file input is attached to DOM', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -159,7 +159,7 @@ describe('input tests', function () {
}); });
}); });
describeFailsFirefox('FileChooser.accept', function () { describe('FileChooser.accept', function () {
it('should accept single file', async () => { it('should accept single file', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -325,7 +325,7 @@ describe('input tests', function () {
}); });
}); });
describeFailsFirefox('FileChooser.cancel', function () { describe('FileChooser.cancel', function () {
it('should cancel dialog', async () => { it('should cancel dialog', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -373,7 +373,7 @@ describe('input tests', function () {
}); });
}); });
describeFailsFirefox('FileChooser.isMultiple', () => { describe('FileChooser.isMultiple', () => {
it('should work for single file pick', async () => { it('should work for single file pick', async () => {
const {page} = getTestState(); const {page} = getTestState();

View File

@ -17,11 +17,11 @@
import expect from 'expect'; import expect from 'expect';
import { import {
getTestState, getTestState,
itFailsFirefox,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
shortWaitForArrayToHaveAtLeastNElements, shortWaitForArrayToHaveAtLeastNElements,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('JSHandle', function () { describe('JSHandle', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -159,7 +159,7 @@ describe('JSHandle', function () {
expect(await bHandle.jsonValue()).toEqual(undefined); expect(await bHandle.jsonValue()).toEqual(undefined);
}); });
itFailsFirefox('should not work with dates', async () => { it('should not work with dates', async () => {
const {page} = getTestState(); const {page} = getTestState();
const dateHandle = await page.evaluateHandle(() => { const dateHandle = await page.evaluateHandle(() => {
@ -409,7 +409,7 @@ describe('JSHandle', function () {
}); });
describe('JSHandle.click', function () { describe('JSHandle.click', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
const clicks: Array<[x: number, y: number]> = []; const clicks: Array<[x: number, y: number]> = [];

View File

@ -21,9 +21,9 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
itFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {KeyInput} from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js'; import {KeyInput} from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js';
import {it} from './mocha-utils.js';
describe('Keyboard', function () { describe('Keyboard', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -45,7 +45,7 @@ describe('Keyboard', function () {
}) })
).toBe(text); ).toBe(text);
}); });
itFailsFirefox('should press the metaKey', async () => { it('should press the metaKey', async () => {
const {page, isFirefox} = getTestState(); const {page, isFirefox} = getTestState();
await page.evaluate(() => { await page.evaluate(() => {
@ -120,22 +120,19 @@ describe('Keyboard', function () {
}) })
).toBe('a'); ).toBe('a');
}); });
itFailsFirefox( it('ElementHandle.press should support |text| option', async () => {
'ElementHandle.press should support |text| option', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html'); await page.goto(server.PREFIX + '/input/textarea.html');
const textarea = (await page.$('textarea'))!; const textarea = (await page.$('textarea'))!;
await textarea.press('a', {text: 'ё'}); await textarea.press('a', {text: 'ё'});
expect( expect(
await page.evaluate(() => { await page.evaluate(() => {
return document.querySelector('textarea')!.value; return document.querySelector('textarea')!.value;
}) })
).toBe('ё'); ).toBe('ё');
} });
); it('should send a character with sendCharacter', async () => {
itFailsFirefox('should send a character with sendCharacter', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html'); await page.goto(server.PREFIX + '/input/textarea.html');
@ -162,7 +159,7 @@ describe('Keyboard', function () {
}) })
).toBe('嗨a'); ).toBe('嗨a');
}); });
itFailsFirefox('should report shiftKey', async () => { it('should report shiftKey', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/keyboard.html'); await page.goto(server.PREFIX + '/input/keyboard.html');
@ -353,7 +350,7 @@ describe('Keyboard', function () {
}) })
).toBe('He Wrd!'); ).toBe('He Wrd!');
}); });
itFailsFirefox('should specify repeat property', async () => { it('should specify repeat property', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html'); await page.goto(server.PREFIX + '/input/textarea.html');
@ -401,7 +398,7 @@ describe('Keyboard', function () {
}) })
).toBe(false); ).toBe(false);
}); });
itFailsFirefox('should type all kinds of characters', async () => { it('should type all kinds of characters', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html'); await page.goto(server.PREFIX + '/input/textarea.html');
@ -410,7 +407,7 @@ describe('Keyboard', function () {
await page.keyboard.type(text); await page.keyboard.type(text);
expect(await page.evaluate('result')).toBe(text); expect(await page.evaluate('result')).toBe(text);
}); });
itFailsFirefox('should specify location', async () => { it('should specify location', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html'); await page.goto(server.PREFIX + '/input/textarea.html');
@ -460,7 +457,7 @@ describe('Keyboard', function () {
}); });
expect(error && error.message).toBe('Unknown key: "😊"'); expect(error && error.message).toBe('Unknown key: "😊"');
}); });
itFailsFirefox('should type emoji', async () => { it('should type emoji', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html'); await page.goto(server.PREFIX + '/input/textarea.html');
@ -471,7 +468,7 @@ describe('Keyboard', function () {
}) })
).toBe('👹 Tokyo street Japan 🇯🇵'); ).toBe('👹 Tokyo street Japan 🇯🇵');
}); });
itFailsFirefox('should type emoji into an iframe', async () => { it('should type emoji into an iframe', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -489,7 +486,7 @@ describe('Keyboard', function () {
}) })
).toBe('👹 Tokyo street Japan 🇯🇵'); ).toBe('👹 Tokyo street Japan 🇯🇵');
}); });
itFailsFirefox('should press the meta key', async () => { it('should press the meta key', async () => {
const {page, isFirefox} = getTestState(); const {page, isFirefox} = getTestState();
await page.evaluate(() => { await page.evaluate(() => {

View File

@ -24,14 +24,9 @@ import {TLSSocket} from 'tls';
import {promisify} from 'util'; import {promisify} from 'util';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js'; import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Product} from '../../lib/cjs/puppeteer/common/Product.js'; import {Product} from '../../lib/cjs/puppeteer/common/Product.js';
import { import {getTestState, itOnlyRegularInstall} from './mocha-utils.js';
getTestState,
itChromeOnly,
itFailsFirefox,
itFirefoxOnly,
itOnlyRegularInstall,
} from './mocha-utils.js';
import utils from './utils.js'; import utils from './utils.js';
import {it} from './mocha-utils.js';
const mkdtempAsync = promisify(fs.mkdtemp); const mkdtempAsync = promisify(fs.mkdtemp);
const readFileAsync = promisify(fs.readFile); const readFileAsync = promisify(fs.readFile);
@ -251,7 +246,7 @@ describe('Launcher specs', function () {
// This might throw. See https://github.com/puppeteer/puppeteer/issues/2778 // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778
await rmAsync(userDataDir).catch(() => {}); await rmAsync(userDataDir).catch(() => {});
}); });
itChromeOnly('tmp profile should be cleaned up', async () => { it('tmp profile should be cleaned up', async () => {
const {defaultBrowserOptions, puppeteer} = getTestState(); const {defaultBrowserOptions, puppeteer} = getTestState();
// Set a custom test tmp dir so that we can validate that // Set a custom test tmp dir so that we can validate that
@ -280,7 +275,7 @@ describe('Launcher specs', function () {
// Restore env var // Restore env var
process.env['PUPPETEER_TMP_DIR'] = ''; process.env['PUPPETEER_TMP_DIR'] = '';
}); });
itFirefoxOnly('userDataDir option restores preferences', async () => { it('userDataDir option restores preferences', async () => {
const {defaultBrowserOptions, puppeteer} = getTestState(); const {defaultBrowserOptions, puppeteer} = getTestState();
const userDataDir = await mkdtempAsync(TMP_FOLDER); const userDataDir = await mkdtempAsync(TMP_FOLDER);
@ -326,7 +321,7 @@ describe('Launcher specs', function () {
// This might throw. See https://github.com/puppeteer/puppeteer/issues/2778 // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778
await rmAsync(userDataDir).catch(() => {}); await rmAsync(userDataDir).catch(() => {});
}); });
itChromeOnly('userDataDir argument with non-existent dir', async () => { it('userDataDir argument with non-existent dir', async () => {
const {isChrome, puppeteer, defaultBrowserOptions} = getTestState(); const {isChrome, puppeteer, defaultBrowserOptions} = getTestState();
const userDataDir = await mkdtempAsync(TMP_FOLDER); const userDataDir = await mkdtempAsync(TMP_FOLDER);
@ -460,49 +455,43 @@ describe('Launcher specs', function () {
await page.close(); await page.close();
await browser.close(); await browser.close();
}); });
itChromeOnly( it('should filter out ignored default arguments in Chrome', async () => {
'should filter out ignored default arguments in Chrome', const {defaultBrowserOptions, puppeteer} = getTestState();
async () => { // Make sure we launch with `--enable-automation` by default.
const {defaultBrowserOptions, puppeteer} = getTestState(); const defaultArgs = puppeteer.defaultArgs();
// Make sure we launch with `--enable-automation` by default. const browser = await puppeteer.launch(
const defaultArgs = puppeteer.defaultArgs(); Object.assign({}, defaultBrowserOptions, {
const browser = await puppeteer.launch( // Ignore first and third default argument.
Object.assign({}, defaultBrowserOptions, { ignoreDefaultArgs: [defaultArgs[0]!, defaultArgs[2]],
// Ignore first and third default argument. })
ignoreDefaultArgs: [defaultArgs[0]!, defaultArgs[2]], );
}) const spawnargs = browser.process()!.spawnargs;
); if (!spawnargs) {
const spawnargs = browser.process()!.spawnargs; throw new Error('spawnargs not present');
if (!spawnargs) {
throw new Error('spawnargs not present');
}
expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1);
expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1);
expect(spawnargs.indexOf(defaultArgs[2]!)).toBe(-1);
await browser.close();
} }
); expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1);
itFirefoxOnly( expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1);
'should filter out ignored default argument in Firefox', expect(spawnargs.indexOf(defaultArgs[2]!)).toBe(-1);
async () => { await browser.close();
const {defaultBrowserOptions, puppeteer} = getTestState(); });
it('should filter out ignored default argument in Firefox', async () => {
const {defaultBrowserOptions, puppeteer} = getTestState();
const defaultArgs = puppeteer.defaultArgs(); const defaultArgs = puppeteer.defaultArgs();
const browser = await puppeteer.launch( const browser = await puppeteer.launch(
Object.assign({}, defaultBrowserOptions, { Object.assign({}, defaultBrowserOptions, {
// Only the first argument is fixed, others are optional. // Only the first argument is fixed, others are optional.
ignoreDefaultArgs: [defaultArgs[0]!], ignoreDefaultArgs: [defaultArgs[0]!],
}) })
); );
const spawnargs = browser.process()!.spawnargs; const spawnargs = browser.process()!.spawnargs;
if (!spawnargs) { if (!spawnargs) {
throw new Error('spawnargs not present'); throw new Error('spawnargs not present');
}
expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1);
expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1);
await browser.close();
} }
); expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1);
expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1);
await browser.close();
});
it('should have default URL when launching browser', async function () { it('should have default URL when launching browser', async function () {
const {defaultBrowserOptions, puppeteer} = getTestState(); const {defaultBrowserOptions, puppeteer} = getTestState();
const browser = await puppeteer.launch(defaultBrowserOptions); const browser = await puppeteer.launch(defaultBrowserOptions);
@ -512,24 +501,21 @@ describe('Launcher specs', function () {
expect(pages).toEqual(['about:blank']); expect(pages).toEqual(['about:blank']);
await browser.close(); await browser.close();
}); });
itFailsFirefox( it('should have custom URL when launching browser', async () => {
'should have custom URL when launching browser', const {server, puppeteer, defaultBrowserOptions} = getTestState();
async () => {
const {server, puppeteer, defaultBrowserOptions} = getTestState();
const options = Object.assign({}, defaultBrowserOptions); const options = Object.assign({}, defaultBrowserOptions);
options.args = [server.EMPTY_PAGE].concat(options.args || []); options.args = [server.EMPTY_PAGE].concat(options.args || []);
const browser = await puppeteer.launch(options); const browser = await puppeteer.launch(options);
const pages = await browser.pages(); const pages = await browser.pages();
expect(pages.length).toBe(1); expect(pages.length).toBe(1);
const page = pages[0]!; const page = pages[0]!;
if (page.url() !== server.EMPTY_PAGE) { if (page.url() !== server.EMPTY_PAGE) {
await page.waitForNavigation(); await page.waitForNavigation();
}
expect(page.url()).toBe(server.EMPTY_PAGE);
await browser.close();
} }
); expect(page.url()).toBe(server.EMPTY_PAGE);
await browser.close();
});
it('should pass the timeout parameter to browser.waitForTarget', async () => { it('should pass the timeout parameter to browser.waitForTarget', async () => {
const {puppeteer, defaultBrowserOptions} = getTestState(); const {puppeteer, defaultBrowserOptions} = getTestState();
const options = Object.assign({}, defaultBrowserOptions, { const options = Object.assign({}, defaultBrowserOptions, {
@ -615,24 +601,21 @@ describe('Launcher specs', function () {
}); });
expect(error.message).toContain('either pipe or debugging port'); expect(error.message).toContain('either pipe or debugging port');
}); });
itChromeOnly( it('should launch Chrome properly with --no-startup-window and waitForInitialPage=false', async () => {
'should launch Chrome properly with --no-startup-window and waitForInitialPage=false', const {defaultBrowserOptions, puppeteer} = getTestState();
async () => { const options = {
const {defaultBrowserOptions, puppeteer} = getTestState(); waitForInitialPage: false,
const options = { // This is needed to prevent Puppeteer from adding an initial blank page.
waitForInitialPage: false, // See also https://github.com/puppeteer/puppeteer/blob/ad6b736039436fcc5c0a262e5b575aa041427be3/src/node/Launcher.ts#L200
// This is needed to prevent Puppeteer from adding an initial blank page. ignoreDefaultArgs: true,
// See also https://github.com/puppeteer/puppeteer/blob/ad6b736039436fcc5c0a262e5b575aa041427be3/src/node/Launcher.ts#L200 ...defaultBrowserOptions,
ignoreDefaultArgs: true, args: ['--no-startup-window'],
...defaultBrowserOptions, };
args: ['--no-startup-window'], const browser = await puppeteer.launch(options);
}; const pages = await browser.pages();
const browser = await puppeteer.launch(options); expect(pages.length).toBe(0);
const pages = await browser.pages(); await browser.close();
expect(pages.length).toBe(0); });
await browser.close();
}
);
}); });
describe('Puppeteer.launch', function () { describe('Puppeteer.launch', function () {
@ -775,7 +758,7 @@ describe('Launcher specs', function () {
}); });
// @see https://github.com/puppeteer/puppeteer/issues/4197 // @see https://github.com/puppeteer/puppeteer/issues/4197
itFailsFirefox('should support targetFilter option', async () => { it('should support targetFilter option', async () => {
const {server, puppeteer, defaultBrowserOptions} = getTestState(); const {server, puppeteer, defaultBrowserOptions} = getTestState();
const originalBrowser = await puppeteer.launch(defaultBrowserOptions); const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
@ -809,68 +792,62 @@ describe('Launcher specs', function () {
.sort() .sort()
).toEqual(['about:blank', server.EMPTY_PAGE]); ).toEqual(['about:blank', server.EMPTY_PAGE]);
}); });
itFailsFirefox( it('should be able to reconnect to a disconnected browser', async () => {
'should be able to reconnect to a disconnected browser', const {server, puppeteer, defaultBrowserOptions} = getTestState();
async () => {
const {server, puppeteer, defaultBrowserOptions} = getTestState();
const originalBrowser = await puppeteer.launch(defaultBrowserOptions); const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
const browserWSEndpoint = originalBrowser.wsEndpoint(); const browserWSEndpoint = originalBrowser.wsEndpoint();
const page = await originalBrowser.newPage(); const page = await originalBrowser.newPage();
await page.goto(server.PREFIX + '/frames/nested-frames.html'); await page.goto(server.PREFIX + '/frames/nested-frames.html');
originalBrowser.disconnect(); originalBrowser.disconnect();
const browser = await puppeteer.connect({browserWSEndpoint}); const browser = await puppeteer.connect({browserWSEndpoint});
const pages = await browser.pages(); const pages = await browser.pages();
const restoredPage = pages.find(page => { const restoredPage = pages.find(page => {
return page.url() === server.PREFIX + '/frames/nested-frames.html'; return page.url() === server.PREFIX + '/frames/nested-frames.html';
})!; })!;
expect(utils.dumpFrames(restoredPage.mainFrame())).toEqual([ expect(utils.dumpFrames(restoredPage.mainFrame())).toEqual([
'http://localhost:<PORT>/frames/nested-frames.html', 'http://localhost:<PORT>/frames/nested-frames.html',
' http://localhost:<PORT>/frames/two-frames.html (2frames)', ' http://localhost:<PORT>/frames/two-frames.html (2frames)',
' http://localhost:<PORT>/frames/frame.html (uno)', ' http://localhost:<PORT>/frames/frame.html (uno)',
' http://localhost:<PORT>/frames/frame.html (dos)', ' http://localhost:<PORT>/frames/frame.html (dos)',
' http://localhost:<PORT>/frames/frame.html (aframe)', ' http://localhost:<PORT>/frames/frame.html (aframe)',
]); ]);
expect( expect(
await restoredPage.evaluate(() => { await restoredPage.evaluate(() => {
return 7 * 8; return 7 * 8;
}) })
).toBe(56); ).toBe(56);
await browser.close(); await browser.close();
} });
);
// @see https://github.com/puppeteer/puppeteer/issues/4197#issuecomment-481793410 // @see https://github.com/puppeteer/puppeteer/issues/4197#issuecomment-481793410
itFailsFirefox( it('should be able to connect to the same page simultaneously', async () => {
'should be able to connect to the same page simultaneously', const {puppeteer, defaultBrowserOptions} = getTestState();
async () => {
const {puppeteer, defaultBrowserOptions} = getTestState();
const browserOne = await puppeteer.launch(defaultBrowserOptions); const browserOne = await puppeteer.launch(defaultBrowserOptions);
const browserTwo = await puppeteer.connect({ const browserTwo = await puppeteer.connect({
browserWSEndpoint: browserOne.wsEndpoint(), browserWSEndpoint: browserOne.wsEndpoint(),
}); });
const [page1, page2] = await Promise.all([ const [page1, page2] = await Promise.all([
new Promise<Page>(x => { new Promise<Page>(x => {
return browserOne.once('targetcreated', target => { return browserOne.once('targetcreated', target => {
return x(target.page()); return x(target.page());
}); });
}), }),
browserTwo.newPage(), browserTwo.newPage(),
]); ]);
expect( expect(
await page1.evaluate(() => { await page1.evaluate(() => {
return 7 * 8; return 7 * 8;
}) })
).toBe(56); ).toBe(56);
expect( expect(
await page2.evaluate(() => { await page2.evaluate(() => {
return 7 * 6; return 7 * 6;
}) })
).toBe(42); ).toBe(42);
await browserOne.close(); await browserOne.close();
} });
);
it('should be able to reconnect', async () => { it('should be able to reconnect', async () => {
const {puppeteer, server, defaultBrowserOptions} = getTestState(); const {puppeteer, server, defaultBrowserOptions} = getTestState();
const browserOne = await puppeteer.launch(defaultBrowserOptions); const browserOne = await puppeteer.launch(defaultBrowserOptions);
@ -933,7 +910,7 @@ describe('Launcher specs', function () {
describe('when the product is chrome, platform is not darwin, and arch is arm64', () => { describe('when the product is chrome, platform is not darwin, and arch is arm64', () => {
describe('and the executable exists', () => { describe('and the executable exists', () => {
itChromeOnly('returns /usr/bin/chromium-browser', async () => { it('returns /usr/bin/chromium-browser', async () => {
const {puppeteer} = getTestState(); const {puppeteer} = getTestState();
const osPlatformStub = sinon.stub(os, 'platform').returns('linux'); const osPlatformStub = sinon.stub(os, 'platform').returns('linux');
const osArchStub = sinon.stub(os, 'arch').returns('arm64'); const osArchStub = sinon.stub(os, 'arch').returns('arm64');
@ -972,33 +949,28 @@ describe('Launcher specs', function () {
}); });
}); });
describe('and the executable does not exist', () => { describe('and the executable does not exist', () => {
itChromeOnly( it('does not return /usr/bin/chromium-browser', async () => {
'does not return /usr/bin/chromium-browser', const {puppeteer} = getTestState();
async () => { const osPlatformStub = sinon.stub(os, 'platform').returns('linux');
const {puppeteer} = getTestState(); const osArchStub = sinon.stub(os, 'arch').returns('arm64');
const osPlatformStub = sinon const fsExistsStub = sinon.stub(fs, 'existsSync');
.stub(os, 'platform') fsExistsStub.withArgs('/usr/bin/chromium-browser').returns(false);
.returns('linux');
const osArchStub = sinon.stub(os, 'arch').returns('arm64');
const fsExistsStub = sinon.stub(fs, 'existsSync');
fsExistsStub.withArgs('/usr/bin/chromium-browser').returns(false);
const executablePath = puppeteer.executablePath(); const executablePath = puppeteer.executablePath();
expect(executablePath).not.toEqual('/usr/bin/chromium-browser'); expect(executablePath).not.toEqual('/usr/bin/chromium-browser');
osPlatformStub.restore(); osPlatformStub.restore();
osArchStub.restore(); osArchStub.restore();
fsExistsStub.restore(); fsExistsStub.restore();
} });
);
}); });
}); });
}); });
}); });
describe('Browser target events', function () { describe('Browser target events', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {server, puppeteer, defaultBrowserOptions} = getTestState(); const {server, puppeteer, defaultBrowserOptions} = getTestState();
const browser = await puppeteer.launch(defaultBrowserOptions); const browser = await puppeteer.launch(defaultBrowserOptions);
@ -1021,51 +993,48 @@ describe('Launcher specs', function () {
}); });
describe('Browser.Events.disconnected', function () { describe('Browser.Events.disconnected', function () {
itFailsFirefox( it('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async () => {
'should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', const {puppeteer, defaultBrowserOptions} = getTestState();
async () => { const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
const {puppeteer, defaultBrowserOptions} = getTestState(); const browserWSEndpoint = originalBrowser.wsEndpoint();
const originalBrowser = await puppeteer.launch(defaultBrowserOptions); const remoteBrowser1 = await puppeteer.connect({
const browserWSEndpoint = originalBrowser.wsEndpoint(); browserWSEndpoint,
const remoteBrowser1 = await puppeteer.connect({ });
browserWSEndpoint, const remoteBrowser2 = await puppeteer.connect({
}); browserWSEndpoint,
const remoteBrowser2 = await puppeteer.connect({ });
browserWSEndpoint,
});
let disconnectedOriginal = 0; let disconnectedOriginal = 0;
let disconnectedRemote1 = 0; let disconnectedRemote1 = 0;
let disconnectedRemote2 = 0; let disconnectedRemote2 = 0;
originalBrowser.on('disconnected', () => { originalBrowser.on('disconnected', () => {
return ++disconnectedOriginal; return ++disconnectedOriginal;
}); });
remoteBrowser1.on('disconnected', () => { remoteBrowser1.on('disconnected', () => {
return ++disconnectedRemote1; return ++disconnectedRemote1;
}); });
remoteBrowser2.on('disconnected', () => { remoteBrowser2.on('disconnected', () => {
return ++disconnectedRemote2; return ++disconnectedRemote2;
}); });
await Promise.all([ await Promise.all([
utils.waitEvent(remoteBrowser2, 'disconnected'), utils.waitEvent(remoteBrowser2, 'disconnected'),
remoteBrowser2.disconnect(), remoteBrowser2.disconnect(),
]); ]);
expect(disconnectedOriginal).toBe(0); expect(disconnectedOriginal).toBe(0);
expect(disconnectedRemote1).toBe(0); expect(disconnectedRemote1).toBe(0);
expect(disconnectedRemote2).toBe(1); expect(disconnectedRemote2).toBe(1);
await Promise.all([ await Promise.all([
utils.waitEvent(remoteBrowser1, 'disconnected'), utils.waitEvent(remoteBrowser1, 'disconnected'),
utils.waitEvent(originalBrowser, 'disconnected'), utils.waitEvent(originalBrowser, 'disconnected'),
originalBrowser.close(), originalBrowser.close(),
]); ]);
expect(disconnectedOriginal).toBe(1); expect(disconnectedOriginal).toBe(1);
expect(disconnectedRemote1).toBe(1); expect(disconnectedRemote1).toBe(1);
expect(disconnectedRemote2).toBe(1); expect(disconnectedRemote2).toBe(1);
} });
);
}); });
}); });

View File

@ -17,7 +17,6 @@
import Protocol from 'devtools-protocol'; import Protocol from 'devtools-protocol';
import expect from 'expect'; import expect from 'expect';
import * as fs from 'fs'; import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import rimraf from 'rimraf'; import rimraf from 'rimraf';
import sinon from 'sinon'; import sinon from 'sinon';
@ -34,6 +33,8 @@ import {
import puppeteer from '../../lib/cjs/puppeteer/puppeteer.js'; import puppeteer from '../../lib/cjs/puppeteer/puppeteer.js';
import {TestServer} from '../../utils/testserver/lib/index.js'; import {TestServer} from '../../utils/testserver/lib/index.js';
import {extendExpectWithToBeGolden} from './utils.js'; import {extendExpectWithToBeGolden} from './utils.js';
import * as Mocha from 'mocha';
import {getTestId} from '../../utils/mochaRunner/lib/utils.js';
const setupServer = async () => { const setupServer = async () => {
const assetsPath = path.join(__dirname, '../assets'); const assetsPath = path.join(__dirname, '../assets');
@ -63,14 +64,14 @@ export const getTestState = (): PuppeteerTestState => {
}; };
const product = const product =
process.env['PRODUCT'] || process.env['PUPPETEER_PRODUCT'] || 'Chromium'; process.env['PRODUCT'] || process.env['PUPPETEER_PRODUCT'] || 'chrome';
const alternativeInstall = process.env['PUPPETEER_ALT_INSTALL'] || false; const alternativeInstall = process.env['PUPPETEER_ALT_INSTALL'] || false;
const headless = (process.env['HEADLESS'] || 'true').trim().toLowerCase(); const headless = (process.env['HEADLESS'] || 'true').trim().toLowerCase();
const isHeadless = headless === 'true' || headless === 'chrome'; const isHeadless = headless === 'true' || headless === 'chrome';
const isFirefox = product === 'firefox'; const isFirefox = product === 'firefox';
const isChrome = product === 'Chromium'; const isChrome = product === 'chrome';
let extraLaunchOptions = {}; let extraLaunchOptions = {};
try { try {
@ -125,7 +126,11 @@ declare module 'expect/build/types' {
} }
const setupGoldenAssertions = (): void => { const setupGoldenAssertions = (): void => {
const suffix = product.toLowerCase(); let suffix = product.toLowerCase();
if (suffix === 'chrome') {
// TODO: to avoid moving golden folders.
suffix = 'chromium';
}
const GOLDEN_DIR = path.join(__dirname, `../golden-${suffix}`); const GOLDEN_DIR = path.join(__dirname, `../golden-${suffix}`);
const OUTPUT_DIR = path.join(__dirname, `../output-${suffix}`); const OUTPUT_DIR = path.join(__dirname, `../output-${suffix}`);
if (fs.existsSync(OUTPUT_DIR)) { if (fs.existsSync(OUTPUT_DIR)) {
@ -152,64 +157,9 @@ interface PuppeteerTestState {
} }
const state: Partial<PuppeteerTestState> = {}; const state: Partial<PuppeteerTestState> = {};
export const itFailsFirefox = (
description: string,
body: Mocha.Func
): Mocha.Test => {
if (isFirefox) {
return xit(description, body);
} else {
return it(description, body);
}
};
export const itChromeOnly = (
description: string,
body: Mocha.Func
): Mocha.Test => {
if (isChrome) {
return it(description, body);
} else {
return xit(description, body);
}
};
export const itHeadlessOnly = (
description: string,
body: Mocha.Func
): Mocha.Test => {
if (isChrome && isHeadless === true) {
return it(description, body);
} else {
return xit(description, body);
}
};
export const itHeadfulOnly = (
description: string,
body: Mocha.Func
): Mocha.Test => {
if (isChrome && isHeadless === false) {
return it(description, body);
} else {
return xit(description, body);
}
};
export const itFirefoxOnly = (
description: string,
body: Mocha.Func
): Mocha.Test => {
if (isFirefox) {
return it(description, body);
} else {
return xit(description, body);
}
};
export const itOnlyRegularInstall = ( export const itOnlyRegularInstall = (
description: string, description: string,
body: Mocha.Func body: Mocha.AsyncFunc
): Mocha.Test => { ): Mocha.Test => {
if (alternativeInstall || process.env['BINARY']) { if (alternativeInstall || process.env['BINARY']) {
return xit(description, body); return xit(description, body);
@ -218,50 +168,10 @@ export const itOnlyRegularInstall = (
} }
}; };
export const itFailsWindowsUntilDate = ( if (
date: Date, process.env['MOCHA_WORKER_ID'] === undefined ||
description: string, process.env['MOCHA_WORKER_ID'] === '0'
body: Mocha.Func ) {
): Mocha.Test => {
if (os.platform() === 'win32' && Date.now() < date.getTime()) {
// we are within the deferred time so skip the test
return xit(description, body);
}
return it(description, body);
};
export const itFailsWindows = (
description: string,
body: Mocha.Func
): Mocha.Test => {
if (os.platform() === 'win32') {
return xit(description, body);
}
return it(description, body);
};
export const describeFailsFirefox = (
description: string,
body: (this: Mocha.Suite) => void
): void | Mocha.Suite => {
if (isFirefox) {
return xdescribe(description, body);
} else {
return describe(description, body);
}
};
export const describeChromeOnly = (
description: string,
body: (this: Mocha.Suite) => void
): Mocha.Suite | void => {
if (isChrome) {
return describe(description, body);
}
};
if (process.env['MOCHA_WORKER_ID'] === '0') {
console.log( console.log(
`Running unit tests with: `Running unit tests with:
-> product: ${product} -> product: ${product}
@ -290,7 +200,7 @@ export const setupTestBrowserHooks = (): void => {
}); });
after(async () => { after(async () => {
await state.browser!.close(); await state.browser?.close();
state.browser = undefined; state.browser = undefined;
}); });
}; };
@ -302,7 +212,7 @@ export const setupTestPageAndContextHooks = (): void => {
}); });
afterEach(async () => { afterEach(async () => {
await state.context!.close(); await state.context?.close();
state.context = undefined; state.context = undefined;
state.page = undefined; state.page = undefined;
}); });
@ -387,3 +297,34 @@ export const shortWaitForArrayToHaveAtLeastNElements = async (
}); });
} }
}; };
type SyncFn = (this: Mocha.Context) => void;
const skippedTests: string[] = process.env['PUPPETEER_SKIPPED_TEST_CONFIG']
? JSON.parse(process.env['PUPPETEER_SKIPPED_TEST_CONFIG'])
: [];
function skipTestIfNeeded(test: Mocha.Test): void {
const testId = getTestId(test.file!, test.fullTitle());
if (
skippedTests.find(skippedTest => {
return testId.startsWith(skippedTest);
})
) {
try {
test.skip();
} catch {}
}
}
export function it(title: string, fn?: Mocha.AsyncFunc | SyncFn): Mocha.Test {
const test = Mocha.it.call(null, title, fn as any);
skipTestIfNeeded(test);
return test;
}
it.only = function (title: string, fn?: Mocha.AsyncFunc | SyncFn): Mocha.Test {
const test = Mocha.it.only.call(null, title, fn as any);
skipTestIfNeeded(test);
return test;
};

View File

@ -19,9 +19,9 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
itFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {KeyInput} from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js'; import {KeyInput} from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js';
import {it} from './mocha-utils.js';
interface Dimensions { interface Dimensions {
x: number; x: number;
@ -115,7 +115,7 @@ describe('Mouse', function () {
}) })
).toBe(text); ).toBe(text);
}); });
itFailsFirefox('should trigger hover state', async () => { it('should trigger hover state', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/scrollable.html'); await page.goto(server.PREFIX + '/input/scrollable.html');
@ -138,24 +138,21 @@ describe('Mouse', function () {
}) })
).toBe('button-91'); ).toBe('button-91');
}); });
itFailsFirefox( it('should trigger hover state with removed window.Node', async () => {
'should trigger hover state with removed window.Node', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/scrollable.html'); await page.goto(server.PREFIX + '/input/scrollable.html');
await page.evaluate(() => {
// @ts-expect-error Expected.
return delete window.Node;
});
await page.hover('#button-6');
expect(
await page.evaluate(() => { await page.evaluate(() => {
// @ts-expect-error Expected. return document.querySelector('button:hover')!.id;
return delete window.Node; })
}); ).toBe('button-6');
await page.hover('#button-6'); });
expect(
await page.evaluate(() => {
return document.querySelector('button:hover')!.id;
})
).toBe('button-6');
}
);
it('should set modifier keys on click', async () => { it('should set modifier keys on click', async () => {
const {page, server, isFirefox} = getTestState(); const {page, server, isFirefox} = getTestState();
@ -202,7 +199,7 @@ describe('Mouse', function () {
} }
} }
}); });
itFailsFirefox('should send mouse wheel events', async () => { it('should send mouse wheel events', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/wheel.html'); await page.goto(server.PREFIX + '/input/wheel.html');
@ -225,7 +222,7 @@ describe('Mouse', function () {
height: 230, height: 230,
}); });
}); });
itFailsFirefox('should tween mouse movement', async () => { it('should tween mouse movement', async () => {
const {page} = getTestState(); const {page} = getTestState();
await page.mouse.move(100, 100); await page.mouse.move(100, 100);

View File

@ -20,12 +20,11 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
itFailsFirefox,
describeFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import os from 'os'; import os from 'os';
import {ServerResponse} from 'http'; import {ServerResponse} from 'http';
import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js'; import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {it} from './mocha-utils.js';
describe('navigation', function () { describe('navigation', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -67,7 +66,7 @@ describe('navigation', function () {
const response = (await page.goto(server.PREFIX + '/historyapi.html'))!; const response = (await page.goto(server.PREFIX + '/historyapi.html'))!;
expect(response.status()).toBe(200); expect(response.status()).toBe(200);
}); });
itFailsFirefox('should work with subframes return 204', async () => { it('should work with subframes return 204', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
server.setRoute('/frames/frame.html', (_req, res) => { server.setRoute('/frames/frame.html', (_req, res) => {
@ -82,7 +81,7 @@ describe('navigation', function () {
}); });
expect(error).toBeUndefined(); expect(error).toBeUndefined();
}); });
itFailsFirefox('should fail when server returns 204', async () => { it('should fail when server returns 204', async () => {
const {page, server, isChrome} = getTestState(); const {page, server, isChrome} = getTestState();
server.setRoute('/empty.html', (_req, res) => { server.setRoute('/empty.html', (_req, res) => {
@ -124,29 +123,23 @@ describe('navigation', function () {
const response = await page.goto(server.PREFIX + '/grid.html'); const response = await page.goto(server.PREFIX + '/grid.html');
expect(response!.status()).toBe(200); expect(response!.status()).toBe(200);
}); });
itFailsFirefox( it('should navigate to empty page with networkidle0', async () => {
'should navigate to empty page with networkidle0', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
const response = await page.goto(server.EMPTY_PAGE, { const response = await page.goto(server.EMPTY_PAGE, {
waitUntil: 'networkidle0', waitUntil: 'networkidle0',
}); });
expect(response!.status()).toBe(200); expect(response!.status()).toBe(200);
} });
); it('should navigate to empty page with networkidle2', async () => {
itFailsFirefox( const {page, server} = getTestState();
'should navigate to empty page with networkidle2',
async () => {
const {page, server} = getTestState();
const response = await page.goto(server.EMPTY_PAGE, { const response = await page.goto(server.EMPTY_PAGE, {
waitUntil: 'networkidle2', waitUntil: 'networkidle2',
}); });
expect(response!.status()).toBe(200); expect(response!.status()).toBe(200);
} });
); it('should fail when navigating to bad url', async () => {
itFailsFirefox('should fail when navigating to bad url', async () => {
const {page, isChrome} = getTestState(); const {page, isChrome} = getTestState();
let error!: Error; let error!: Error;
@ -175,7 +168,7 @@ describe('navigation', function () {
: 'net::ERR_CERT_AUTHORITY_INVALID'; : 'net::ERR_CERT_AUTHORITY_INVALID';
} }
itFailsFirefox('should fail when navigating to bad SSL', async () => { it('should fail when navigating to bad SSL', async () => {
const {page, httpsServer, isChrome} = getTestState(); const {page, httpsServer, isChrome} = getTestState();
// Make sure that network events do not emit 'undefined'. // Make sure that network events do not emit 'undefined'.
@ -311,7 +304,7 @@ describe('navigation', function () {
const response = (await page.goto(server.EMPTY_PAGE))!; const response = (await page.goto(server.EMPTY_PAGE))!;
expect(response.ok()).toBe(true); expect(response.ok()).toBe(true);
}); });
itFailsFirefox('should work when navigating to data url', async () => { it('should work when navigating to data url', async () => {
const {page} = getTestState(); const {page} = getTestState();
const response = (await page.goto('data:text/html,hello'))!; const response = (await page.goto('data:text/html,hello'))!;
@ -334,85 +327,79 @@ describe('navigation', function () {
expect(response.ok()).toBe(true); expect(response.ok()).toBe(true);
expect(response.url()).toBe(server.EMPTY_PAGE); expect(response.url()).toBe(server.EMPTY_PAGE);
}); });
itFailsFirefox( it('should wait for network idle to succeed navigation', async () => {
'should wait for network idle to succeed navigation', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
let responses: ServerResponse[] = []; let responses: ServerResponse[] = [];
// Hold on to a bunch of requests without answering. // Hold on to a bunch of requests without answering.
server.setRoute('/fetch-request-a.js', (_req, res) => { server.setRoute('/fetch-request-a.js', (_req, res) => {
return responses.push(res); return responses.push(res);
}); });
server.setRoute('/fetch-request-b.js', (_req, res) => { server.setRoute('/fetch-request-b.js', (_req, res) => {
return responses.push(res); return responses.push(res);
}); });
server.setRoute('/fetch-request-c.js', (_req, res) => { server.setRoute('/fetch-request-c.js', (_req, res) => {
return responses.push(res); return responses.push(res);
}); });
server.setRoute('/fetch-request-d.js', (_req, res) => { server.setRoute('/fetch-request-d.js', (_req, res) => {
return responses.push(res); return responses.push(res);
}); });
const initialFetchResourcesRequested = Promise.all([ const initialFetchResourcesRequested = Promise.all([
server.waitForRequest('/fetch-request-a.js'), server.waitForRequest('/fetch-request-a.js'),
server.waitForRequest('/fetch-request-b.js'), server.waitForRequest('/fetch-request-b.js'),
server.waitForRequest('/fetch-request-c.js'), server.waitForRequest('/fetch-request-c.js'),
]); ]);
const secondFetchResourceRequested = server.waitForRequest( const secondFetchResourceRequested = server.waitForRequest(
'/fetch-request-d.js' '/fetch-request-d.js'
); );
// Navigate to a page which loads immediately and then does a bunch of // Navigate to a page which loads immediately and then does a bunch of
// requests via javascript's fetch method. // requests via javascript's fetch method.
const navigationPromise = page.goto( const navigationPromise = page.goto(server.PREFIX + '/networkidle.html', {
server.PREFIX + '/networkidle.html', waitUntil: 'networkidle0',
{ });
waitUntil: 'networkidle0', // Track when the navigation gets completed.
} let navigationFinished = false;
); navigationPromise.then(() => {
// Track when the navigation gets completed. return (navigationFinished = true);
let navigationFinished = false; });
navigationPromise.then(() => {
return (navigationFinished = true);
});
// Wait for the page's 'load' event. // Wait for the page's 'load' event.
await new Promise(fulfill => { await new Promise(fulfill => {
return page.once('load', fulfill); return page.once('load', fulfill);
}); });
expect(navigationFinished).toBe(false); expect(navigationFinished).toBe(false);
// Wait for the initial three resources to be requested. // Wait for the initial three resources to be requested.
await initialFetchResourcesRequested; await initialFetchResourcesRequested;
// Expect navigation still to be not finished. // Expect navigation still to be not finished.
expect(navigationFinished).toBe(false); expect(navigationFinished).toBe(false);
// Respond to initial requests. // Respond to initial requests.
for (const response of responses) { for (const response of responses) {
response.statusCode = 404; response.statusCode = 404;
response.end(`File not found`); response.end(`File not found`);
}
// Reset responses array
responses = [];
// Wait for the second round to be requested.
await secondFetchResourceRequested;
// Expect navigation still to be not finished.
expect(navigationFinished).toBe(false);
// Respond to requests.
for (const response of responses) {
response.statusCode = 404;
response.end(`File not found`);
}
const response = (await navigationPromise)!;
// Expect navigation to succeed.
expect(response.ok()).toBe(true);
} }
);
// Reset responses array
responses = [];
// Wait for the second round to be requested.
await secondFetchResourceRequested;
// Expect navigation still to be not finished.
expect(navigationFinished).toBe(false);
// Respond to requests.
for (const response of responses) {
response.statusCode = 404;
response.end(`File not found`);
}
const response = (await navigationPromise)!;
// Expect navigation to succeed.
expect(response.ok()).toBe(true);
});
it('should not leak listeners during navigation', async () => { it('should not leak listeners during navigation', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -461,38 +448,32 @@ describe('navigation', function () {
process.removeListener('warning', warningHandler); process.removeListener('warning', warningHandler);
expect(warning).toBe(null); expect(warning).toBe(null);
}); });
itFailsFirefox( it('should navigate to dataURL and fire dataURL requests', async () => {
'should navigate to dataURL and fire dataURL requests', const {page} = getTestState();
async () => {
const {page} = getTestState();
const requests: HTTPRequest[] = []; const requests: HTTPRequest[] = [];
page.on('request', request => { page.on('request', request => {
return !utils.isFavicon(request) && requests.push(request); return !utils.isFavicon(request) && requests.push(request);
}); });
const dataURL = 'data:text/html,<div>yo</div>'; const dataURL = 'data:text/html,<div>yo</div>';
const response = (await page.goto(dataURL))!; const response = (await page.goto(dataURL))!;
expect(response.status()).toBe(200); expect(response.status()).toBe(200);
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0]!.url()).toBe(dataURL); expect(requests[0]!.url()).toBe(dataURL);
} });
); it('should navigate to URL with hash and fire requests without hash', async () => {
itFailsFirefox( const {page, server} = getTestState();
'should navigate to URL with hash and fire requests without hash',
async () => {
const {page, server} = getTestState();
const requests: HTTPRequest[] = []; const requests: HTTPRequest[] = [];
page.on('request', request => { page.on('request', request => {
return !utils.isFavicon(request) && requests.push(request); return !utils.isFavicon(request) && requests.push(request);
}); });
const response = (await page.goto(server.EMPTY_PAGE + '#hash'))!; const response = (await page.goto(server.EMPTY_PAGE + '#hash'))!;
expect(response.status()).toBe(200); expect(response.status()).toBe(200);
expect(response.url()).toBe(server.EMPTY_PAGE); expect(response.url()).toBe(server.EMPTY_PAGE);
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0]!.url()).toBe(server.EMPTY_PAGE); expect(requests[0]!.url()).toBe(server.EMPTY_PAGE);
} });
);
it('should work with self requesting page', async () => { it('should work with self requesting page', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -512,7 +493,7 @@ describe('navigation', function () {
} }
expect(error.message).toContain(url); expect(error.message).toContain(url);
}); });
itFailsFirefox('should send referer', async () => { it('should send referer', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
const [request1, request2] = await Promise.all([ const [request1, request2] = await Promise.all([
@ -582,7 +563,7 @@ describe('navigation', function () {
expect(response).toBe(null); expect(response).toBe(null);
expect(page.url()).toBe(server.EMPTY_PAGE + '#foobar'); expect(page.url()).toBe(server.EMPTY_PAGE + '#foobar');
}); });
itFailsFirefox('should work with history.pushState()', async () => { it('should work with history.pushState()', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -599,7 +580,7 @@ describe('navigation', function () {
expect(response).toBe(null); expect(response).toBe(null);
expect(page.url()).toBe(server.PREFIX + '/wow.html'); expect(page.url()).toBe(server.PREFIX + '/wow.html');
}); });
itFailsFirefox('should work with history.replaceState()', async () => { it('should work with history.replaceState()', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -616,13 +597,11 @@ describe('navigation', function () {
expect(response).toBe(null); expect(response).toBe(null);
expect(page.url()).toBe(server.PREFIX + '/replaced.html'); expect(page.url()).toBe(server.PREFIX + '/replaced.html');
}); });
itFailsFirefox( it('should work with DOM history.back()/history.forward()', async () => {
'should work with DOM history.back()/history.forward()', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setContent(` await page.setContent(`
<a id=back onclick='javascript:goBack()'>back</a> <a id=back onclick='javascript:goBack()'>back</a>
<a id=forward onclick='javascript:goForward()'>forward</a> <a id=forward onclick='javascript:goForward()'>forward</a>
<script> <script>
@ -632,46 +611,42 @@ describe('navigation', function () {
history.pushState({}, '', '/second.html'); history.pushState({}, '', '/second.html');
</script> </script>
`); `);
expect(page.url()).toBe(server.PREFIX + '/second.html'); expect(page.url()).toBe(server.PREFIX + '/second.html');
const [backResponse] = await Promise.all([ const [backResponse] = await Promise.all([
page.waitForNavigation(), page.waitForNavigation(),
page.click('a#back'), page.click('a#back'),
]); ]);
expect(backResponse).toBe(null); expect(backResponse).toBe(null);
expect(page.url()).toBe(server.PREFIX + '/first.html'); expect(page.url()).toBe(server.PREFIX + '/first.html');
const [forwardResponse] = await Promise.all([ const [forwardResponse] = await Promise.all([
page.waitForNavigation(), page.waitForNavigation(),
page.click('a#forward'), page.click('a#forward'),
]); ]);
expect(forwardResponse).toBe(null); expect(forwardResponse).toBe(null);
expect(page.url()).toBe(server.PREFIX + '/second.html'); expect(page.url()).toBe(server.PREFIX + '/second.html');
} });
); it('should work when subframe issues window.stop()', async () => {
itFailsFirefox( const {page, server} = getTestState();
'should work when subframe issues window.stop()',
async () => {
const {page, server} = getTestState();
server.setRoute('/frames/style.css', () => {}); server.setRoute('/frames/style.css', () => {});
const navigationPromise = page.goto( const navigationPromise = page.goto(
server.PREFIX + '/frames/one-frame.html' server.PREFIX + '/frames/one-frame.html'
); );
const frame = await utils.waitEvent(page, 'frameattached'); const frame = await utils.waitEvent(page, 'frameattached');
await new Promise<void>(fulfill => { await new Promise<void>(fulfill => {
page.on('framenavigated', f => { page.on('framenavigated', f => {
if (f === frame) { if (f === frame) {
fulfill(); fulfill();
} }
});
}); });
await Promise.all([ });
frame.evaluate(() => { await Promise.all([
return window.stop(); frame.evaluate(() => {
}), return window.stop();
navigationPromise, }),
]); navigationPromise,
} ]);
); });
}); });
describe('Page.goBack', function () { describe('Page.goBack', function () {
@ -692,7 +667,7 @@ describe('navigation', function () {
response = (await page.goForward())!; response = (await page.goForward())!;
expect(response).toBe(null); expect(response).toBe(null);
}); });
itFailsFirefox('should work with HistoryAPI', async () => { it('should work with HistoryAPI', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -711,7 +686,7 @@ describe('navigation', function () {
}); });
}); });
describeFailsFirefox('Frame.goto', function () { describe('Frame.goto', function () {
it('should navigate subframes', async () => { it('should navigate subframes', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -776,7 +751,7 @@ describe('navigation', function () {
}); });
}); });
describeFailsFirefox('Frame.waitForNavigation', function () { describe('Frame.waitForNavigation', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();

View File

@ -22,14 +22,11 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
itFailsFirefox,
describeFailsFirefox,
itChromeOnly,
itFirefoxOnly,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js'; import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js'; import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js';
import {ServerResponse} from 'http'; import {ServerResponse} from 'http';
import {it} from './mocha-utils.js';
describe('network', function () { describe('network', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -83,7 +80,7 @@ describe('network', function () {
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0]!.frame()).toBe(page.mainFrame()); expect(requests[0]!.frame()).toBe(page.mainFrame());
}); });
itFailsFirefox('should work for subframe navigation request', async () => { it('should work for subframe navigation request', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
(await page.goto(server.EMPTY_PAGE))!; (await page.goto(server.EMPTY_PAGE))!;
@ -115,13 +112,13 @@ describe('network', function () {
}); });
describe('Request.headers', function () { describe('Request.headers', function () {
itChromeOnly('should define Chrome as user agent header', async () => { it('should define Chrome as user agent header', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
const response = (await page.goto(server.EMPTY_PAGE))!; const response = (await page.goto(server.EMPTY_PAGE))!;
expect(response.request().headers()['user-agent']).toContain('Chrome'); expect(response.request().headers()['user-agent']).toContain('Chrome');
}); });
itFirefoxOnly('should define Firefox as user agent header', async () => { it('should define Firefox as user agent header', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
const response = (await page.goto(server.EMPTY_PAGE))!; const response = (await page.goto(server.EMPTY_PAGE))!;
@ -142,7 +139,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Request.initiator', () => { describe('Request.initiator', () => {
it('should return the initiator', async () => { it('should return the initiator', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -187,7 +184,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Response.fromCache', function () { describe('Response.fromCache', function () {
it('should return |false| for non-cached content', async () => { it('should return |false| for non-cached content', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -218,7 +215,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Response.fromServiceWorker', function () { describe('Response.fromServiceWorker', function () {
it('should return |false| for non-service-worker content', async () => { it('should return |false| for non-service-worker content', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -253,7 +250,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Request.postData', function () { describe('Request.postData', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -284,7 +281,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Response.text', function () { describe('Response.text', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -367,7 +364,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Response.json', function () { describe('Response.json', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -376,7 +373,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Response.buffer', function () { describe('Response.buffer', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -461,7 +458,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Response.timing', function () { describe('Response.timing', function () {
it('returns timing information', async () => { it('returns timing information', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
const responses: HTTPResponse[] = []; const responses: HTTPResponse[] = [];
@ -474,7 +471,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Network Events', function () { describe('Network Events', function () {
it('Page.Events.Request', async () => { it('Page.Events.Request', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -624,7 +621,7 @@ describe('network', function () {
}); });
describe('Request.isNavigationRequest', () => { describe('Request.isNavigationRequest', () => {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
const requests = new Map(); const requests = new Map();
@ -639,7 +636,7 @@ describe('network', function () {
expect(requests.get('script.js').isNavigationRequest()).toBe(false); expect(requests.get('script.js').isNavigationRequest()).toBe(false);
expect(requests.get('style.css').isNavigationRequest()).toBe(false); expect(requests.get('style.css').isNavigationRequest()).toBe(false);
}); });
itFailsFirefox('should work with request interception', async () => { it('should work with request interception', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
const requests = new Map(); const requests = new Map();
@ -656,7 +653,7 @@ describe('network', function () {
expect(requests.get('script.js').isNavigationRequest()).toBe(false); expect(requests.get('script.js').isNavigationRequest()).toBe(false);
expect(requests.get('style.css').isNavigationRequest()).toBe(false); expect(requests.get('style.css').isNavigationRequest()).toBe(false);
}); });
itFailsFirefox('should work when navigating to image', async () => { it('should work when navigating to image', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
const requests: HTTPRequest[] = []; const requests: HTTPRequest[] = [];
@ -668,7 +665,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Page.setExtraHTTPHeaders', function () { describe('Page.setExtraHTTPHeaders', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -697,7 +694,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('Page.authenticate', function () { describe('Page.authenticate', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -794,7 +791,7 @@ describe('network', function () {
}); });
}); });
describeFailsFirefox('raw network headers', async () => { describe('raw network headers', async () => {
it('Same-origin set-cookie navigation', async () => { it('Same-origin set-cookie navigation', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();

View File

@ -16,18 +16,15 @@
import utils from './utils.js'; import utils from './utils.js';
import expect from 'expect'; import expect from 'expect';
import { import {getTestState} from './mocha-utils.js';
getTestState,
describeChromeOnly,
itFailsFirefox,
} from './mocha-utils.js';
import { import {
Browser, Browser,
BrowserContext, BrowserContext,
} from '../../lib/cjs/puppeteer/common/Browser.js'; } from '../../lib/cjs/puppeteer/common/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js'; import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {it} from './mocha-utils.js';
describeChromeOnly('OOPIF', function () { describe('OOPIF', function () {
/* We use a special browser for this test as we need the --site-per-process flag */ /* We use a special browser for this test as we need the --site-per-process flag */
let browser: Browser; let browser: Browser;
let context: BrowserContext; let context: BrowserContext;
@ -207,6 +204,7 @@ describeChromeOnly('OOPIF', function () {
await utils.navigateFrame(page, 'frame1', server.EMPTY_PAGE); await utils.navigateFrame(page, 'frame1', server.EMPTY_PAGE);
expect(frame.url()).toBe(server.EMPTY_PAGE); expect(frame.url()).toBe(server.EMPTY_PAGE);
}); });
it('should support evaluating in oop iframes', async () => { it('should support evaluating in oop iframes', async () => {
const {server} = getTestState(); const {server} = getTestState();
@ -420,7 +418,7 @@ describeChromeOnly('OOPIF', function () {
browser1.disconnect(); browser1.disconnect();
}); });
itFailsFirefox('should support lazy OOP frames', async () => { it('should support lazy OOP frames', async () => {
const {server} = getTestState(); const {server} = getTestState();
await page.goto(server.PREFIX + '/lazy-oopif-frame.html'); await page.goto(server.PREFIX + '/lazy-oopif-frame.html');

View File

@ -22,13 +22,12 @@ import {CDPSession} from '../../lib/cjs/puppeteer/common/Connection.js';
import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js'; import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
import {Metrics, Page} from '../../lib/cjs/puppeteer/common/Page.js'; import {Metrics, Page} from '../../lib/cjs/puppeteer/common/Page.js';
import { import {
describeFailsFirefox,
getTestState, getTestState,
itFailsFirefox,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
import utils, {attachFrame, waitEvent} from './utils.js'; import utils, {attachFrame, waitEvent} from './utils.js';
import {it} from './mocha-utils.js';
describe('Page', function () { describe('Page', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -59,7 +58,7 @@ describe('Page', function () {
await newPage.close(); await newPage.close();
expect(await browser.pages()).not.toContain(newPage); expect(await browser.pages()).not.toContain(newPage);
}); });
itFailsFirefox('should run beforeunload if asked for', async () => { it('should run beforeunload if asked for', async () => {
const {context, server, isChrome} = getTestState(); const {context, server, isChrome} = getTestState();
const newPage = await context.newPage(); const newPage = await context.newPage();
@ -79,7 +78,7 @@ describe('Page', function () {
await dialog.accept(); await dialog.accept();
await pageClosingPromise; await pageClosingPromise;
}); });
itFailsFirefox('should *not* run beforeunload by default', async () => { it('should *not* run beforeunload by default', async () => {
const {context, server} = getTestState(); const {context, server} = getTestState();
const newPage = await context.newPage(); const newPage = await context.newPage();
@ -97,7 +96,7 @@ describe('Page', function () {
await newPage.close(); await newPage.close();
expect(newPage.isClosed()).toBe(true); expect(newPage.isClosed()).toBe(true);
}); });
itFailsFirefox('should terminate network waiters', async () => { it('should terminate network waiters', async () => {
const {context, server} = getTestState(); const {context, server} = getTestState();
const newPage = await context.newPage(); const newPage = await context.newPage();
@ -182,7 +181,7 @@ describe('Page', function () {
}); });
}); });
describeFailsFirefox('Page.Events.error', function () { describe('Page.Events.error', function () {
it('should throw when page crashes', async () => { it('should throw when page crashes', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -196,7 +195,7 @@ describe('Page', function () {
}); });
}); });
describeFailsFirefox('Page.Events.Popup', function () { describe('Page.Events.Popup', function () {
it('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -354,7 +353,7 @@ describe('Page', function () {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(await getPermission(page, 'geolocation')).toBe('prompt'); expect(await getPermission(page, 'geolocation')).toBe('prompt');
}); });
itFailsFirefox('should deny permission when not listed', async () => { it('should deny permission when not listed', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -374,14 +373,14 @@ describe('Page', function () {
}); });
expect(error.message).toBe('Unknown permission: foo'); expect(error.message).toBe('Unknown permission: foo');
}); });
itFailsFirefox('should grant permission when listed', async () => { it('should grant permission when listed', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await context.overridePermissions(server.EMPTY_PAGE, ['geolocation']); await context.overridePermissions(server.EMPTY_PAGE, ['geolocation']);
expect(await getPermission(page, 'geolocation')).toBe('granted'); expect(await getPermission(page, 'geolocation')).toBe('granted');
}); });
itFailsFirefox('should reset permissions', async () => { it('should reset permissions', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -390,7 +389,7 @@ describe('Page', function () {
await context.clearPermissionOverrides(); await context.clearPermissionOverrides();
expect(await getPermission(page, 'geolocation')).toBe('prompt'); expect(await getPermission(page, 'geolocation')).toBe('prompt');
}); });
itFailsFirefox('should trigger permission onchange', async () => { it('should trigger permission onchange', async () => {
const {page, server, context, isHeadless} = getTestState(); const {page, server, context, isHeadless} = getTestState();
// TODO: re-enable this test in headful once crbug.com/1324480 rolls out. // TODO: re-enable this test in headful once crbug.com/1324480 rolls out.
@ -434,33 +433,30 @@ describe('Page', function () {
}) })
).toEqual(['prompt', 'denied', 'granted', 'prompt']); ).toEqual(['prompt', 'denied', 'granted', 'prompt']);
}); });
itFailsFirefox( it('should isolate permissions between browser contexts', async () => {
'should isolate permissions between browser contexts', const {page, server, context, browser} = getTestState();
async () => {
const {page, server, context, browser} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const otherContext = await browser.createIncognitoBrowserContext(); const otherContext = await browser.createIncognitoBrowserContext();
const otherPage = await otherContext.newPage(); const otherPage = await otherContext.newPage();
await otherPage.goto(server.EMPTY_PAGE); await otherPage.goto(server.EMPTY_PAGE);
expect(await getPermission(page, 'geolocation')).toBe('prompt'); expect(await getPermission(page, 'geolocation')).toBe('prompt');
expect(await getPermission(otherPage, 'geolocation')).toBe('prompt'); expect(await getPermission(otherPage, 'geolocation')).toBe('prompt');
await context.overridePermissions(server.EMPTY_PAGE, []); await context.overridePermissions(server.EMPTY_PAGE, []);
await otherContext.overridePermissions(server.EMPTY_PAGE, [ await otherContext.overridePermissions(server.EMPTY_PAGE, [
'geolocation', 'geolocation',
]); ]);
expect(await getPermission(page, 'geolocation')).toBe('denied'); expect(await getPermission(page, 'geolocation')).toBe('denied');
expect(await getPermission(otherPage, 'geolocation')).toBe('granted'); expect(await getPermission(otherPage, 'geolocation')).toBe('granted');
await context.clearPermissionOverrides(); await context.clearPermissionOverrides();
expect(await getPermission(page, 'geolocation')).toBe('prompt'); expect(await getPermission(page, 'geolocation')).toBe('prompt');
expect(await getPermission(otherPage, 'geolocation')).toBe('granted'); expect(await getPermission(otherPage, 'geolocation')).toBe('granted');
await otherContext.close(); await otherContext.close();
} });
); it('should grant persistent-storage', async () => {
itFailsFirefox('should grant persistent-storage', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -475,7 +471,7 @@ describe('Page', function () {
}); });
describe('Page.setGeolocation', function () { describe('Page.setGeolocation', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
await context.overridePermissions(server.PREFIX, ['geolocation']); await context.overridePermissions(server.PREFIX, ['geolocation']);
@ -509,7 +505,7 @@ describe('Page', function () {
}); });
}); });
describeFailsFirefox('Page.setOfflineMode', function () { describe('Page.setOfflineMode', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -547,7 +543,7 @@ describe('Page', function () {
}); });
describe('ExecutionContext.queryObjects', function () { describe('ExecutionContext.queryObjects', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
// Instantiate an object // Instantiate an object
@ -567,7 +563,7 @@ describe('Page', function () {
}, objectsHandle); }, objectsHandle);
expect(values).toEqual(['hello', 'world']); expect(values).toEqual(['hello', 'world']);
}); });
itFailsFirefox('should work for non-blank page', async () => { it('should work for non-blank page', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
// Instantiate an object // Instantiate an object
@ -613,7 +609,7 @@ describe('Page', function () {
}); });
}); });
describeFailsFirefox('Page.Events.Console', function () { describe('Page.Events.Console', function () {
it('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -802,7 +798,7 @@ describe('Page', function () {
}); });
}); });
describeFailsFirefox('Page.metrics', function () { describe('Page.metrics', function () {
it('should get metrics from a page', async () => { it('should get metrics from a page', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -1108,7 +1104,7 @@ describe('Page', function () {
}); });
}); });
describeFailsFirefox('Page.exposeFunction', function () { describe('Page.exposeFunction', function () {
it('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -1266,7 +1262,7 @@ describe('Page', function () {
}); });
}); });
describeFailsFirefox('Page.Events.PageError', function () { describe('Page.Events.PageError', function () {
it('should fire', async () => { it('should fire', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -1329,7 +1325,7 @@ describe('Page', function () {
}) })
).toContain('iPhone'); ).toContain('iPhone');
}); });
itFailsFirefox('should work with additional userAgentMetdata', async () => { it('should work with additional userAgentMetdata', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setUserAgent('MockBrowser', { await page.setUserAgent('MockBrowser', {
@ -1496,7 +1492,7 @@ describe('Page', function () {
}); });
}); });
describeFailsFirefox('Page.setBypassCSP', function () { describe('Page.setBypassCSP', function () {
it('should bypass CSP meta tag', async () => { it('should bypass CSP meta tag', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -1879,21 +1875,18 @@ describe('Page', function () {
).toBe('rgb(0, 128, 0)'); ).toBe('rgb(0, 128, 0)');
}); });
itFailsFirefox( it('should throw when added with content to the CSP page', async () => {
'should throw when added with content to the CSP page', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.PREFIX + '/csp.html'); await page.goto(server.PREFIX + '/csp.html');
let error!: Error; let error!: Error;
await page await page
.addStyleTag({content: 'body { background-color: green; }'}) .addStyleTag({content: 'body { background-color: green; }'})
.catch(error_ => { .catch(error_ => {
return (error = error_); return (error = error_);
}); });
expect(error).toBeTruthy(); expect(error).toBeTruthy();
} });
);
it('should throw when added with URL to the CSP page', async () => { it('should throw when added with URL to the CSP page', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -1921,7 +1914,7 @@ describe('Page', function () {
}); });
}); });
describeFailsFirefox('Page.setJavaScriptEnabled', function () { describe('Page.setJavaScriptEnabled', function () {
it('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -1962,23 +1955,20 @@ describe('Page', function () {
]); ]);
expect(nonCachedRequest.headers['if-modified-since']).toBe(undefined); expect(nonCachedRequest.headers['if-modified-since']).toBe(undefined);
}); });
itFailsFirefox( it('should stay disabled when toggling request interception on/off', async () => {
'should stay disabled when toggling request interception on/off', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.setCacheEnabled(false); await page.setCacheEnabled(false);
await page.setRequestInterception(true); await page.setRequestInterception(true);
await page.setRequestInterception(false); await page.setRequestInterception(false);
await page.goto(server.PREFIX + '/cached/one-style.html'); await page.goto(server.PREFIX + '/cached/one-style.html');
const [nonCachedRequest] = await Promise.all([ const [nonCachedRequest] = await Promise.all([
server.waitForRequest('/cached/one-style.html'), server.waitForRequest('/cached/one-style.html'),
page.reload(), page.reload(),
]); ]);
expect(nonCachedRequest.headers['if-modified-since']).toBe(undefined); expect(nonCachedRequest.headers['if-modified-since']).toBe(undefined);
} });
);
}); });
describe('printing to PDF', function () { describe('printing to PDF', function () {
@ -2220,33 +2210,30 @@ describe('Page', function () {
expect(error.message).toContain('Values must be strings'); expect(error.message).toContain('Values must be strings');
}); });
// @see https://github.com/puppeteer/puppeteer/issues/3327 // @see https://github.com/puppeteer/puppeteer/issues/3327
itFailsFirefox( it('should work when re-defining top-level Event class', async () => {
'should work when re-defining top-level Event class', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/select.html'); await page.goto(server.PREFIX + '/input/select.html');
await page.evaluate(() => {
// @ts-expect-error Expected.
return (window.Event = undefined);
});
await page.select('select', 'blue');
expect(
await page.evaluate(() => { await page.evaluate(() => {
// @ts-expect-error Expected. return (globalThis as any).result.onInput;
return (window.Event = undefined); })
}); ).toEqual(['blue']);
await page.select('select', 'blue'); expect(
expect( await page.evaluate(() => {
await page.evaluate(() => { return (globalThis as any).result.onChange;
return (globalThis as any).result.onInput; })
}) ).toEqual(['blue']);
).toEqual(['blue']); });
expect(
await page.evaluate(() => {
return (globalThis as any).result.onChange;
})
).toEqual(['blue']);
}
);
}); });
describe('Page.Events.Close', function () { describe('Page.Events.Close', function () {
itFailsFirefox('should work with window.close', async () => { it('should work with window.close', async () => {
const {page, context} = getTestState(); const {page, context} = getTestState();
const newPagePromise = new Promise<Page>(fulfill => { const newPagePromise = new Promise<Page>(fulfill => {

View File

@ -17,15 +17,12 @@
import expect from 'expect'; import expect from 'expect';
import http from 'http'; import http from 'http';
import os from 'os'; import os from 'os';
import { import {getTestState} from './mocha-utils.js';
getTestState,
describeFailsFirefox,
itFailsWindows,
} from './mocha-utils.js';
import type {Server, IncomingMessage, ServerResponse} from 'http'; import type {Server, IncomingMessage, ServerResponse} from 'http';
import type {Browser} from '../../lib/cjs/puppeteer/common/Browser.js'; import type {Browser} from '../../lib/cjs/puppeteer/common/Browser.js';
import type {AddressInfo} from 'net'; import type {AddressInfo} from 'net';
import {TestServer} from '../../utils/testserver/lib/index.js'; import {TestServer} from '../../utils/testserver/lib/index.js';
import {it} from './mocha-utils.js';
let HOSTNAME = os.hostname(); let HOSTNAME = os.hostname();
@ -53,7 +50,7 @@ function getEmptyPageUrl(server: TestServer): string {
return `http://${HOSTNAME}:${server.PORT}${emptyPagePath}`; return `http://${HOSTNAME}:${server.PORT}${emptyPagePath}`;
} }
describeFailsFirefox('request proxy', () => { describe('request proxy', () => {
let browser: Browser; let browser: Browser;
let proxiedRequestUrls: string[]; let proxiedRequestUrls: string[];
let proxyServer: Server; let proxyServer: Server;
@ -194,28 +191,25 @@ describeFailsFirefox('request proxy', () => {
/** /**
* See issues #7873, #7719, and #7698. * See issues #7873, #7719, and #7698.
*/ */
itFailsWindows( it('should proxy requests when configured at context level', async () => {
'should proxy requests when configured at context level', const {puppeteer, defaultBrowserOptions, server} = getTestState();
async () => { const emptyPageUrl = getEmptyPageUrl(server);
const {puppeteer, defaultBrowserOptions, server} = getTestState();
const emptyPageUrl = getEmptyPageUrl(server);
browser = await puppeteer.launch({ browser = await puppeteer.launch({
...defaultBrowserOptions, ...defaultBrowserOptions,
args: defaultArgs, args: defaultArgs,
}); });
const context = await browser.createIncognitoBrowserContext({ const context = await browser.createIncognitoBrowserContext({
proxyServer: proxyServerUrl, proxyServer: proxyServerUrl,
}); });
const page = await context.newPage(); const page = await context.newPage();
const response = (await page.goto(emptyPageUrl))!; const response = (await page.goto(emptyPageUrl))!;
expect(response.ok()).toBe(true); expect(response.ok()).toBe(true);
expect(proxiedRequestUrls).toEqual([emptyPageUrl]); expect(proxiedRequestUrls).toEqual([emptyPageUrl]);
} });
);
it('should respect proxy bypass list when configured at context level', async () => { it('should respect proxy bypass list when configured at context level', async () => {
const {puppeteer, defaultBrowserOptions, server} = getTestState(); const {puppeteer, defaultBrowserOptions, server} = getTestState();

View File

@ -20,6 +20,7 @@ import {
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Query handler tests', function () { describe('Query handler tests', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();

View File

@ -20,6 +20,7 @@ import {
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('querySelector', function () { describe('querySelector', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();

View File

@ -22,7 +22,6 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
describeFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js'; import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
import { import {
@ -30,11 +29,12 @@ import {
HTTPRequest, HTTPRequest,
InterceptResolutionAction, InterceptResolutionAction,
} from '../../lib/cjs/puppeteer/common/HTTPRequest.js'; } from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {it} from './mocha-utils.js';
describe('request interception', function () { describe('request interception', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
describeFailsFirefox('Page.setRequestInterception', function () { describe('Page.setRequestInterception', function () {
const expectedActions: ActionResult[] = ['abort', 'continue', 'respond']; const expectedActions: ActionResult[] = ['abort', 'continue', 'respond'];
while (expectedActions.length > 0) { while (expectedActions.length > 0) {
const expectedAction = expectedActions.pop(); const expectedAction = expectedActions.pop();
@ -709,7 +709,7 @@ describe('request interception', function () {
}); });
}); });
describeFailsFirefox('Request.continue', function () { describe('Request.continue', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -805,7 +805,7 @@ describe('request interception', function () {
}); });
}); });
describeFailsFirefox('Request.respond', function () { describe('Request.respond', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();

View File

@ -22,15 +22,15 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
describeFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js'; import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js'; import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
import {it} from './mocha-utils.js';
describe('request interception', function () { describe('request interception', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
describeFailsFirefox('Page.setRequestInterception', function () { describe('Page.setRequestInterception', function () {
it('should intercept', async () => { it('should intercept', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -623,7 +623,7 @@ describe('request interception', function () {
}); });
}); });
describeFailsFirefox('Request.continue', function () { describe('Request.continue', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -739,7 +739,7 @@ describe('request interception', function () {
}); });
}); });
describeFailsFirefox('Request.respond', function () { describe('Request.respond', function () {
it('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();

View File

@ -19,17 +19,15 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
itFailsFirefox,
itHeadfulOnly,
itChromeOnly,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Screenshots', function () { describe('Screenshots', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
describe('Page.screenshot', function () { describe('Page.screenshot', function () {
itFailsFirefox('should work', async () => { it('should work', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
@ -37,7 +35,7 @@ describe('Screenshots', function () {
const screenshot = await page.screenshot(); const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-sanity.png'); expect(screenshot).toBeGolden('screenshot-sanity.png');
}); });
itFailsFirefox('should clip rect', async () => { it('should clip rect', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
@ -52,7 +50,7 @@ describe('Screenshots', function () {
}); });
expect(screenshot).toBeGolden('screenshot-clip-rect.png'); expect(screenshot).toBeGolden('screenshot-clip-rect.png');
}); });
itFailsFirefox('should use scale for clip', async () => { it('should use scale for clip', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
@ -68,23 +66,20 @@ describe('Screenshots', function () {
}); });
expect(screenshot).toBeGolden('screenshot-clip-rect-scale2.png'); expect(screenshot).toBeGolden('screenshot-clip-rect-scale2.png');
}); });
itFailsFirefox( it('should get screenshot bigger than the viewport', async () => {
'should get screenshot bigger than the viewport', const {page, server} = getTestState();
async () => { await page.setViewport({width: 50, height: 50});
const {page, server} = getTestState(); await page.goto(server.PREFIX + '/grid.html');
await page.setViewport({width: 50, height: 50}); const screenshot = await page.screenshot({
await page.goto(server.PREFIX + '/grid.html'); clip: {
const screenshot = await page.screenshot({ x: 25,
clip: { y: 25,
x: 25, width: 100,
y: 25, height: 100,
width: 100, },
height: 100, });
}, expect(screenshot).toBeGolden('screenshot-offscreen-clip.png');
}); });
expect(screenshot).toBeGolden('screenshot-offscreen-clip.png');
}
);
it('should run in parallel', async () => { it('should run in parallel', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
@ -106,7 +101,7 @@ describe('Screenshots', function () {
const screenshots = await Promise.all(promises); const screenshots = await Promise.all(promises);
expect(screenshots[1]!).toBeGolden('grid-cell-1.png'); expect(screenshots[1]!).toBeGolden('grid-cell-1.png');
}); });
itFailsFirefox('should take fullPage screenshots', async () => { it('should take fullPage screenshots', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
@ -147,7 +142,7 @@ describe('Screenshots', function () {
}) })
); );
}); });
itFailsFirefox('should allow transparency', async () => { it('should allow transparency', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 100, height: 100}); await page.setViewport({width: 100, height: 100});
@ -155,7 +150,7 @@ describe('Screenshots', function () {
const screenshot = await page.screenshot({omitBackground: true}); const screenshot = await page.screenshot({omitBackground: true});
expect(screenshot).toBeGolden('transparent.png'); expect(screenshot).toBeGolden('transparent.png');
}); });
itFailsFirefox('should render white background on jpeg file', async () => { it('should render white background on jpeg file', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 100, height: 100}); await page.setViewport({width: 100, height: 100});
@ -166,7 +161,7 @@ describe('Screenshots', function () {
}); });
expect(screenshot).toBeGolden('white.jpg'); expect(screenshot).toBeGolden('white.jpg');
}); });
itFailsFirefox('should work with webp', async () => { it('should work with webp', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 100, height: 100}); await page.setViewport({width: 100, height: 100});
@ -190,7 +185,7 @@ describe('Screenshots', function () {
}); });
expect(screenshot).toBeGolden('screenshot-clip-odd-size.png'); expect(screenshot).toBeGolden('screenshot-clip-odd-size.png');
}); });
itFailsFirefox('should return base64', async () => { it('should return base64', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
@ -206,7 +201,7 @@ describe('Screenshots', function () {
'screenshot-sanity.png' 'screenshot-sanity.png'
); );
}); });
itHeadfulOnly('should work in "fromSurface: false" mode', async () => { it('should work in "fromSurface: false" mode', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
@ -231,7 +226,7 @@ describe('Screenshots', function () {
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-bounding-box.png'); expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
}); });
itChromeOnly('should work with a null viewport', async () => { it('should work with a null viewport', async () => {
const {defaultBrowserOptions, puppeteer, server} = getTestState(); const {defaultBrowserOptions, puppeteer, server} = getTestState();
const browser = await puppeteer.launch({ const browser = await puppeteer.launch({
@ -271,14 +266,12 @@ describe('Screenshots', function () {
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-padding-border.png'); expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
}); });
itFailsFirefox( it('should capture full element when larger than viewport', async () => {
'should capture full element when larger than viewport', const {page} = getTestState();
async () => {
const {page} = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.setContent(` await page.setContent(`
something above something above
<style> <style>
div.to-screenshot { div.to-screenshot {
@ -293,22 +286,21 @@ describe('Screenshots', function () {
</style> </style>
<div class="to-screenshot"></div> <div class="to-screenshot"></div>
`); `);
const elementHandle = (await page.$('div.to-screenshot'))!; const elementHandle = (await page.$('div.to-screenshot'))!;
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden( expect(screenshot).toBeGolden(
'screenshot-element-larger-than-viewport.png' 'screenshot-element-larger-than-viewport.png'
); );
expect( expect(
await page.evaluate(() => { await page.evaluate(() => {
return { return {
w: window.innerWidth, w: window.innerWidth,
h: window.innerHeight, h: window.innerHeight,
}; };
}) })
).toEqual({w: 500, h: 500}); ).toEqual({w: 500, h: 500});
} });
);
it('should scroll element into view', async () => { it('should scroll element into view', async () => {
const {page} = getTestState(); const {page} = getTestState();
@ -336,7 +328,7 @@ describe('Screenshots', function () {
'screenshot-element-scrolled-into-view.png' 'screenshot-element-scrolled-into-view.png'
); );
}); });
itFailsFirefox('should work with a rotated element', async () => { it('should work with a rotated element', async () => {
const {page} = getTestState(); const {page} = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
@ -351,7 +343,7 @@ describe('Screenshots', function () {
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-rotate.png'); expect(screenshot).toBeGolden('screenshot-element-rotate.png');
}); });
itFailsFirefox('should fail to screenshot a detached element', async () => { it('should fail to screenshot a detached element', async () => {
const {page} = getTestState(); const {page} = getTestState();
await page.setContent('<h1>remove this</h1>'); await page.setContent('<h1>remove this</h1>');
@ -386,7 +378,7 @@ describe('Screenshots', function () {
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-fractional.png'); expect(screenshot).toBeGolden('screenshot-element-fractional.png');
}); });
itFailsFirefox('should work for an element with an offset', async () => { it('should work for an element with an offset', async () => {
const {page} = getTestState(); const {page} = getTestState();
await page.setContent( await page.setContent(

View File

@ -20,11 +20,12 @@ import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Target} from '../../lib/cjs/puppeteer/common/Target.js'; import {Target} from '../../lib/cjs/puppeteer/common/Target.js';
import { import {
getTestState, getTestState,
itFailsFirefox,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
import utils from './utils.js'; import utils from './utils.js';
import {it} from './mocha-utils.js';
const {waitEvent} = utils; const {waitEvent} = utils;
describe('Target', function () { describe('Target', function () {
@ -79,7 +80,7 @@ describe('Target', function () {
).toBe('Hello world'); ).toBe('Hello world');
expect(await originalPage.$('body')).toBeTruthy(); expect(await originalPage.$('body')).toBeTruthy();
}); });
itFailsFirefox('should be able to use async waitForTarget', async () => { it('should be able to use async waitForTarget', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
const [otherPage] = await Promise.all([ const [otherPage] = await Promise.all([
@ -101,89 +102,83 @@ describe('Target', function () {
); );
expect(page).not.toEqual(otherPage); expect(page).not.toEqual(otherPage);
}); });
itFailsFirefox( it('should report when a new page is created and closed', async () => {
'should report when a new page is created and closed', const {page, server, context} = getTestState();
async () => {
const {page, server, context} = getTestState();
const [otherPage] = await Promise.all([ const [otherPage] = await Promise.all([
context context
.waitForTarget(target => { .waitForTarget(target => {
return target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html'; return target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html';
})
.then(target => {
return target.page();
}),
page.evaluate((url: string) => {
return window.open(url);
}, server.CROSS_PROCESS_PREFIX + '/empty.html'),
]);
expect(otherPage!.url()).toContain(server.CROSS_PROCESS_PREFIX);
expect(
await otherPage!.evaluate(() => {
return ['Hello', 'world'].join(' ');
}) })
).toBe('Hello world'); .then(target => {
expect(await otherPage!.$('body')).toBeTruthy();
let allPages = await context.pages();
expect(allPages).toContain(page);
expect(allPages).toContain(otherPage);
const closePagePromise = new Promise(fulfill => {
return context.once('targetdestroyed', target => {
return fulfill(target.page());
});
});
await otherPage!.close();
expect(await closePagePromise).toBe(otherPage);
allPages = (await Promise.all(
context.targets().map(target => {
return target.page(); return target.page();
}) }),
)) as Page[]; page.evaluate((url: string) => {
expect(allPages).toContain(page); return window.open(url);
expect(allPages).not.toContain(otherPage); }, server.CROSS_PROCESS_PREFIX + '/empty.html'),
} ]);
); expect(otherPage!.url()).toContain(server.CROSS_PROCESS_PREFIX);
itFailsFirefox( expect(
'should report when a service worker is created and destroyed', await otherPage!.evaluate(() => {
async () => { return ['Hello', 'world'].join(' ');
const {page, server, context} = getTestState(); })
).toBe('Hello world');
expect(await otherPage!.$('body')).toBeTruthy();
await page.goto(server.EMPTY_PAGE); let allPages = await context.pages();
const createdTarget = new Promise<Target>(fulfill => { expect(allPages).toContain(page);
return context.once('targetcreated', target => { expect(allPages).toContain(otherPage);
return fulfill(target);
}); const closePagePromise = new Promise(fulfill => {
return context.once('targetdestroyed', target => {
return fulfill(target.page());
}); });
});
await otherPage!.close();
expect(await closePagePromise).toBe(otherPage);
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html'); allPages = (await Promise.all(
context.targets().map(target => {
return target.page();
})
)) as Page[];
expect(allPages).toContain(page);
expect(allPages).not.toContain(otherPage);
});
it('should report when a service worker is created and destroyed', async () => {
const {page, server, context} = getTestState();
expect((await createdTarget).type()).toBe('service_worker'); await page.goto(server.EMPTY_PAGE);
expect((await createdTarget).url()).toBe( const createdTarget = new Promise<Target>(fulfill => {
server.PREFIX + '/serviceworkers/empty/sw.js' return context.once('targetcreated', target => {
); return fulfill(target);
const destroyedTarget = new Promise(fulfill => {
return context.once('targetdestroyed', target => {
return fulfill(target);
});
}); });
await page.evaluate(() => { });
return (
globalThis as unknown as { await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
registrationPromise: Promise<{unregister: () => void}>;
} expect((await createdTarget).type()).toBe('service_worker');
).registrationPromise.then((registration: any) => { expect((await createdTarget).url()).toBe(
return registration.unregister(); server.PREFIX + '/serviceworkers/empty/sw.js'
}); );
const destroyedTarget = new Promise(fulfill => {
return context.once('targetdestroyed', target => {
return fulfill(target);
}); });
expect(await destroyedTarget).toBe(await createdTarget); });
} await page.evaluate(() => {
); return (
itFailsFirefox('should create a worker from a service worker', async () => { globalThis as unknown as {
registrationPromise: Promise<{unregister: () => void}>;
}
).registrationPromise.then((registration: any) => {
return registration.unregister();
});
});
expect(await destroyedTarget).toBe(await createdTarget);
});
it('should create a worker from a service worker', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html'); await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
@ -198,7 +193,7 @@ describe('Target', function () {
}) })
).toBe('[object ServiceWorkerGlobalScope]'); ).toBe('[object ServiceWorkerGlobalScope]');
}); });
itFailsFirefox('should create a worker from a shared worker', async () => { it('should create a worker from a shared worker', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -215,7 +210,7 @@ describe('Target', function () {
}) })
).toBe('[object SharedWorkerGlobalScope]'); ).toBe('[object SharedWorkerGlobalScope]');
}); });
itFailsFirefox('should report when a target url changes', async () => { it('should report when a target url changes', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -235,7 +230,7 @@ describe('Target', function () {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect((await changedTarget).url()).toBe(server.EMPTY_PAGE); expect((await changedTarget).url()).toBe(server.EMPTY_PAGE);
}); });
itFailsFirefox('should not report uninitialized pages', async () => { it('should not report uninitialized pages', async () => {
const {context} = getTestState(); const {context} = getTestState();
let targetChanged = false; let targetChanged = false;
@ -268,37 +263,34 @@ describe('Target', function () {
expect(targetChanged).toBe(false); expect(targetChanged).toBe(false);
context.removeListener('targetchanged', listener); context.removeListener('targetchanged', listener);
}); });
itFailsFirefox( it('should not crash while redirecting if original request was missed', async () => {
'should not crash while redirecting if original request was missed', const {page, server, context} = getTestState();
async () => {
const {page, server, context} = getTestState();
let serverResponse!: ServerResponse; let serverResponse!: ServerResponse;
server.setRoute('/one-style.css', (_req, res) => { server.setRoute('/one-style.css', (_req, res) => {
return (serverResponse = res); return (serverResponse = res);
}); });
// Open a new page. Use window.open to connect to the page later. // Open a new page. Use window.open to connect to the page later.
await Promise.all([ await Promise.all([
page.evaluate((url: string) => { page.evaluate((url: string) => {
return window.open(url); return window.open(url);
}, server.PREFIX + '/one-style.html'), }, server.PREFIX + '/one-style.html'),
server.waitForRequest('/one-style.css'), server.waitForRequest('/one-style.css'),
]); ]);
// Connect to the opened page. // Connect to the opened page.
const target = await context.waitForTarget(target => { const target = await context.waitForTarget(target => {
return target.url().includes('one-style.html'); return target.url().includes('one-style.html');
}); });
const newPage = (await target.page())!; const newPage = (await target.page())!;
// Issue a redirect. // Issue a redirect.
serverResponse.writeHead(302, {location: '/injectedstyle.css'}); serverResponse.writeHead(302, {location: '/injectedstyle.css'});
serverResponse.end(); serverResponse.end();
// Wait for the new page to load. // Wait for the new page to load.
await waitEvent(newPage, 'load'); await waitEvent(newPage, 'load');
// Cleanup. // Cleanup.
await newPage.close(); await newPage.close();
} });
); it('should have an opener', async () => {
itFailsFirefox('should have an opener', async () => {
const {page, server, context} = getTestState(); const {page, server, context} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -318,7 +310,7 @@ describe('Target', function () {
}); });
describe('Browser.waitForTarget', () => { describe('Browser.waitForTarget', () => {
itFailsFirefox('should wait for a target', async () => { it('should wait for a target', async () => {
const {browser, puppeteer, server} = getTestState(); const {browser, puppeteer, server} = getTestState();
let resolved = false; let resolved = false;

View File

@ -19,10 +19,10 @@ import {
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
describeFailsFirefox,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {it} from './mocha-utils.js';
describeFailsFirefox('Touchscreen', function () { describe('Touchscreen', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();

View File

@ -17,11 +17,12 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import expect from 'expect'; import expect from 'expect';
import {getTestState, describeChromeOnly} from './mocha-utils.js'; import {getTestState} from './mocha-utils.js';
import {Browser} from '../../lib/cjs/puppeteer/common/Browser.js'; import {Browser} from '../../lib/cjs/puppeteer/common/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js'; import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {it} from './mocha-utils.js';
describeChromeOnly('Tracing', function () { describe('Tracing', function () {
let outputFile!: string; let outputFile!: string;
let browser!: Browser; let browser!: Browser;
let page!: Page; let page!: Page;

View File

@ -18,11 +18,11 @@ import expect from 'expect';
import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js'; import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js';
import { import {
getTestState, getTestState,
itFailsFirefox,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {attachFrame, detachFrame} from './utils.js'; import {attachFrame, detachFrame} from './utils.js';
import {it} from './mocha-utils.js';
describe('waittask specs', function () { describe('waittask specs', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -188,7 +188,7 @@ describe('waittask specs', function () {
}); });
await watchdog; await watchdog;
}); });
itFailsFirefox('should work with strict CSP policy', async () => { it('should work with strict CSP policy', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
server.setCSP('/empty.html', 'script-src ' + server.PREFIX); server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
@ -421,7 +421,7 @@ describe('waittask specs', function () {
await frame.waitForSelector('div'); await frame.waitForSelector('div');
}); });
itFailsFirefox('should work with removed MutationObserver', async () => { it('should work with removed MutationObserver', async () => {
const {page} = getTestState(); const {page} = getTestState();
await page.evaluate(() => { await page.evaluate(() => {
@ -465,23 +465,20 @@ describe('waittask specs', function () {
await watchdog; await watchdog;
}); });
itFailsFirefox( it('Page.waitForSelector is shortcut for main frame', async () => {
'Page.waitForSelector is shortcut for main frame', const {page, server} = getTestState();
async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await attachFrame(page, 'frame1', server.EMPTY_PAGE); await attachFrame(page, 'frame1', server.EMPTY_PAGE);
const otherFrame = page.frames()[1]!; const otherFrame = page.frames()[1]!;
const watchdog = page.waitForSelector('div'); const watchdog = page.waitForSelector('div');
await otherFrame.evaluate(addElement, 'div'); await otherFrame.evaluate(addElement, 'div');
await page.evaluate(addElement, 'div'); await page.evaluate(addElement, 'div');
const eHandle = await watchdog; const eHandle = await watchdog;
expect(eHandle?.frame).toBe(page.mainFrame()); expect(eHandle?.frame).toBe(page.mainFrame());
} });
);
itFailsFirefox('should run in specified frame', async () => { it('should run in specified frame', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await attachFrame(page, 'frame1', server.EMPTY_PAGE); await attachFrame(page, 'frame1', server.EMPTY_PAGE);
@ -495,7 +492,7 @@ describe('waittask specs', function () {
expect(eHandle?.frame).toBe(frame2); expect(eHandle?.frame).toBe(frame2);
}); });
itFailsFirefox('should throw when frame is detached', async () => { it('should throw when frame is detached', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await attachFrame(page, 'frame1', server.EMPTY_PAGE); await attachFrame(page, 'frame1', server.EMPTY_PAGE);
@ -738,7 +735,7 @@ describe('waittask specs', function () {
'waiting for selector `.//div` failed: timeout 10ms exceeded' 'waiting for selector `.//div` failed: timeout 10ms exceeded'
); );
}); });
itFailsFirefox('should run in specified frame', async () => { it('should run in specified frame', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await attachFrame(page, 'frame1', server.EMPTY_PAGE); await attachFrame(page, 'frame1', server.EMPTY_PAGE);
@ -751,7 +748,7 @@ describe('waittask specs', function () {
const eHandle = await waitForXPathPromise; const eHandle = await waitForXPathPromise;
expect(eHandle?.frame).toBe(frame2); expect(eHandle?.frame).toBe(frame2);
}); });
itFailsFirefox('should throw when frame is detached', async () => { it('should throw when frame is detached', async () => {
const {page, server} = getTestState(); const {page, server} = getTestState();
await attachFrame(page, 'frame1', server.EMPTY_PAGE); await attachFrame(page, 'frame1', server.EMPTY_PAGE);

View File

@ -18,14 +18,14 @@ import expect from 'expect';
import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js'; import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
import {WebWorker} from '../../lib/cjs/puppeteer/common/WebWorker.js'; import {WebWorker} from '../../lib/cjs/puppeteer/common/WebWorker.js';
import { import {
describeFailsFirefox,
getTestState, getTestState,
setupTestBrowserHooks, setupTestBrowserHooks,
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
import {waitEvent} from './utils.js'; import {waitEvent} from './utils.js';
import {it} from './mocha-utils.js';
describeFailsFirefox('Workers', function () { describe('Workers', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
it('Page.workers', async () => { it('Page.workers', async () => {

View File

@ -9,7 +9,8 @@
}, },
"include": ["src"], "include": ["src"],
"references": [ "references": [
{"path": "../tsconfig.lib.json"}, {"path": "../src/tsconfig.cjs.json"},
{"path": "../utils/testserver/tsconfig.json"} {"path": "../utils/testserver/tsconfig.json"},
{"path": "../utils/mochaRunner/tsconfig.json"}
] ]
} }

View File

@ -0,0 +1,47 @@
# Mocha Runner
Mocha Runner is a test runner on top of mocha. It uses `/test/TestSuites.json` and `/test/TestExpectations.json` files to run mocha tests in multiple configurations and interpret results.
## Running tests for Mocha Runner itself.
```
npm run build:dev && npx c8 node utils/mochaRunner/lib/test.js
```
## Running tests using Mocha Runner
```
npm run build:dev && npm run test
```
By default, the runner runs all test suites applicable to the current platform.
To pick a test suite, provide the `--test-suite` arguments. For example,
```
npm run build:dev && npm run test -- --test-suite chrome-headless
```
## TestSuites.json
Define test suites via the `testSuites` attribute. `parameters` can be used in the `TestExpectations.json` to disable tests
based on parameters. The meaning for parameters is defined in `parameterDefinitons` which tell what env object corresponds
to the given parameter.
## TestExpectations.json
An expectation looks like this:
```
{
"testIdPattern": "[accessibility.spec]",
"platforms": ["darwin", "win32", "linux"],
"parameters": ["firefox"],
"expectations": ["SKIP"]
}
```
`testIdPattern` defines a string that will be used to prefix-match tests. `platforms` defines the platforms the expectation is for (`or`-logic).
`parameters` defines the parameters that the test has to match (`and`-logic). `expectations` is the list of test results that are considered to be acceptable.
Currently, expectations are updated manually. The test runner outputs the suggested changes to the expectation file if the test run does not match
expectations.

View File

@ -0,0 +1,204 @@
import {
TestExpectation,
MochaResults,
zTestSuiteFile,
zPlatform,
TestSuite,
TestSuiteFile,
Platform,
} from './types.js';
import path from 'path';
import fs from 'fs';
import os from 'os';
import {spawn} from 'node:child_process';
import {
extendProcessEnv,
filterByPlatform,
prettyPrintJSON,
readJSON,
filterByParameters,
getExpectationUpdates,
getSkippedTests,
} from './utils.js';
function getApplicableTestSuites(
parsedSuitesFile: TestSuiteFile,
platform: Platform
): TestSuite[] {
const testSuiteArgIdx = process.argv.indexOf('--test-suite');
let applicableSuites: TestSuite[] = [];
if (testSuiteArgIdx === -1) {
applicableSuites = filterByPlatform(parsedSuitesFile.testSuites, platform);
} else {
const testSuiteId = process.argv[testSuiteArgIdx + 1];
const testSuite = parsedSuitesFile.testSuites.find(suite => {
return suite.id === testSuiteId;
});
if (!testSuite) {
console.error(`Test suite ${testSuiteId} is not defined`);
process.exit(1);
}
if (!testSuite.platforms.includes(platform)) {
console.warn(
`Test suite ${testSuiteId} is not enabled for your platform. Running it anyway.`
);
}
applicableSuites = [testSuite];
}
return applicableSuites;
}
async function main() {
const platform = zPlatform.parse(os.platform());
const expectations = readJSON(
path.join(process.cwd(), 'test', 'TestExpectations.json')
) as TestExpectation[];
const parsedSuitesFile = zTestSuiteFile.parse(
readJSON(path.join(process.cwd(), 'test', 'TestSuites.json'))
);
const applicableSuites = getApplicableTestSuites(parsedSuitesFile, platform);
console.log('Planning to run the following test suites', applicableSuites);
let fail = false;
const recommendations = [];
try {
for (const suite of applicableSuites) {
const parameters = suite.parameters;
const applicableExpectations = filterByParameters(
filterByPlatform(expectations, platform),
parameters
);
const skippedTests = getSkippedTests(applicableExpectations);
const env = extendProcessEnv([
...parameters.map(param => {
return parsedSuitesFile.parameterDefinitons[param];
}),
{
PUPPETEER_SKIPPED_TEST_CONFIG: JSON.stringify(
skippedTests.map(ex => {
return ex.testIdPattern;
})
),
},
]);
const tmpDir = fs.mkdtempSync(
path.join(os.tmpdir(), 'puppeteer-test-runner-')
);
const tmpFilename = path.join(tmpDir, 'output.json');
console.log('Running', JSON.stringify(parameters), tmpFilename);
const handle = spawn(
'npx mocha',
['--reporter=json', '--reporter-option', 'output=' + tmpFilename],
{
shell: true,
cwd: process.cwd(),
stdio: 'inherit',
env,
}
);
await new Promise<void>((resolve, reject) => {
handle.on('error', err => {
reject(err);
});
handle.on('close', () => {
resolve();
});
});
console.log('Finished', JSON.stringify(parameters));
try {
const results = readJSON(tmpFilename) as MochaResults;
console.log('Results from mocha');
console.log('Stats', JSON.stringify(results.stats));
results.pending.length > 0 && console.log('# Pending tests');
for (const test of results.pending) {
console.log(`\t? ${test.fullTitle} ${test.file}`);
}
results.failures.length > 0 && console.log('# Failed tests');
for (const test of results.failures) {
console.log(`\tF ${test.fullTitle} ${test.file}`, test.err);
}
const recommendation = getExpectationUpdates(
results,
applicableExpectations,
{
platforms: [os.platform()],
parameters,
}
);
if (recommendation.length > 0) {
fail = true;
recommendations.push(...recommendation);
} else {
console.log('Test run matches expecations');
continue;
}
} catch (err) {
fail = true;
console.error(err);
}
}
} catch (err) {
fail = true;
console.error(err);
} finally {
const toAdd = recommendations.filter(item => {
return item.action === 'add';
});
if (toAdd.length) {
console.log(
'Add the following to TestExpecations.json to ignore the error:'
);
prettyPrintJSON(
toAdd.map(item => {
return item.expectation;
})
);
}
const toRemove = recommendations.filter(item => {
return item.action === 'remove';
});
if (toRemove.length) {
console.log(
'Remove the following from the TestExpecations.json to ignore the error:'
);
prettyPrintJSON(
toRemove.map(item => {
return item.expectation;
})
);
}
const toUpdate = recommendations.filter(item => {
return item.action === 'update';
});
if (toUpdate.length) {
console.log(
'Update the following expectations in the TestExpecations.json to ignore the error:'
);
prettyPrintJSON(
toUpdate.map(item => {
return item.expectation;
})
);
}
process.exit(fail ? 1 : 0);
}
}
main().catch(error => {
console.error(error);
process.exit(1);
});

View File

@ -0,0 +1,46 @@
import assert from 'assert/strict';
import test from 'node:test';
import {filterByParameters, getTestResultForFailure} from './utils.js';
import {TestExpectation} from './types.js';
import {getFilename, extendProcessEnv} from './utils.js';
test('extendProcessEnv', () => {
const env = extendProcessEnv([{TEST: 'TEST'}, {TEST2: 'TEST2'}]);
assert.equal(env['TEST'], 'TEST');
assert.equal(env['TEST2'], 'TEST2');
});
test('getFilename', () => {
assert.equal(getFilename('/etc/test.ts'), 'test');
assert.equal(getFilename('/etc/test.js'), 'test');
});
test('getTestResultForFailure', () => {
assert.equal(
getTestResultForFailure({err: {code: 'ERR_MOCHA_TIMEOUT'}}),
'TIMEOUT'
);
assert.equal(getTestResultForFailure({err: {code: 'ERROR'}}), 'FAIL');
});
test('filterByParameters', () => {
const expectations: TestExpectation[] = [
{
testIdPattern:
'[oopif.spec] OOPIF "after all" hook for "should keep track of a frames OOP state"',
platforms: ['darwin'],
parameters: ['firefox', 'headless'],
expectations: ['FAIL'],
},
];
assert.equal(
filterByParameters(expectations, ['firefox', 'headless']).length,
1
);
assert.equal(filterByParameters(expectations, ['firefox']).length, 0);
assert.equal(
filterByParameters(expectations, ['firefox', 'headless', 'other']).length,
1
);
assert.equal(filterByParameters(expectations, ['other']).length, 0);
});

View File

@ -0,0 +1,42 @@
import {z} from 'zod';
export const zPlatform = z.enum(['win32', 'linux', 'darwin']);
export type Platform = z.infer<typeof zPlatform>;
export const zTestSuite = z.object({
id: z.string(),
platforms: z.array(zPlatform),
parameters: z.array(z.string()),
});
export type TestSuite = z.infer<typeof zTestSuite>;
export const zTestSuiteFile = z.object({
testSuites: z.array(zTestSuite),
parameterDefinitons: z.record(z.any()),
});
export type TestSuiteFile = z.infer<typeof zTestSuiteFile>;
export type TestResult = 'PASS' | 'FAIL' | 'TIMEOUT' | 'SKIP';
export type TestExpectation = {
testIdPattern: string;
platforms: NodeJS.Platform[];
parameters: string[];
expectations: TestResult[];
};
export type MochaTestResult = {
fullTitle: string;
file: string;
err?: {code: string};
};
export type MochaResults = {
stats: unknown;
pending: MochaTestResult[];
passes: MochaTestResult[];
failures: MochaTestResult[];
};

View File

@ -0,0 +1,153 @@
import {
MochaTestResult,
TestExpectation,
MochaResults,
TestResult,
} from './types.js';
import path from 'path';
import fs from 'fs';
export function extendProcessEnv(envs: object[]): NodeJS.ProcessEnv {
return envs.reduce(
(acc: object, item: object) => {
Object.assign(acc, item);
return acc;
},
{
...process.env,
}
) as NodeJS.ProcessEnv;
}
export function getFilename(file: string): string {
return path.basename(file).replace(path.extname(file), '');
}
export function readJSON(path: string): unknown {
return JSON.parse(fs.readFileSync(path, 'utf-8'));
}
export function filterByPlatform<T extends {platforms: NodeJS.Platform[]}>(
items: T[],
platform: NodeJS.Platform
): T[] {
return items.filter(item => {
return item.platforms.includes(platform);
});
}
export function prettyPrintJSON(json: unknown): void {
console.log(JSON.stringify(json, null, 2));
}
export function filterByParameters(
expecations: TestExpectation[],
parameters: string[]
): TestExpectation[] {
const querySet = new Set(parameters);
return expecations.filter(ex => {
return ex.parameters.every(param => {
return querySet.has(param);
});
});
}
/**
* The last expectation that matches the startsWith filter wins.
*/
export function findEffectiveExpecationForTest(
expectations: TestExpectation[],
result: MochaTestResult
): TestExpectation | undefined {
return expectations
.filter(expecation => {
if (
getTestId(result.file, result.fullTitle).startsWith(
expecation.testIdPattern
)
) {
return true;
}
return false;
})
.pop();
}
type RecommendedExpecation = {
expectation: TestExpectation;
test: MochaTestResult;
action: 'remove' | 'add' | 'update';
};
export function getExpectationUpdates(
results: MochaResults,
expecations: TestExpectation[],
context: {
platforms: NodeJS.Platform[];
parameters: string[];
}
): RecommendedExpecation[] {
const output: RecommendedExpecation[] = [];
for (const pass of results.passes) {
const expectation = findEffectiveExpecationForTest(expecations, pass);
if (expectation && !expectation.expectations.includes('PASS')) {
output.push({
expectation,
test: pass,
action: 'remove',
});
}
}
for (const failure of results.failures) {
const expectation = findEffectiveExpecationForTest(expecations, failure);
if (expectation) {
if (
!expectation.expectations.includes(getTestResultForFailure(failure))
) {
output.push({
expectation: {
...expectation,
expectations: [
...expectation.expectations,
getTestResultForFailure(failure),
],
},
test: failure,
action: 'update',
});
}
} else {
output.push({
expectation: {
testIdPattern: getTestId(failure.file, failure.fullTitle),
platforms: context.platforms,
parameters: context.parameters,
expectations: [getTestResultForFailure(failure)],
},
test: failure,
action: 'add',
});
}
}
return output;
}
export function getTestResultForFailure(
test: Pick<MochaTestResult, 'err'>
): TestResult {
return test.err?.code === 'ERR_MOCHA_TIMEOUT' ? 'TIMEOUT' : 'FAIL';
}
export function getSkippedTests(
expectations: TestExpectation[]
): TestExpectation[] {
return expectations.filter(ex => {
return ex.expectations.includes('SKIP');
});
}
export function getTestId(file: string, fullTitle: string): string {
return `[${getFilename(file)}] ${fullTitle}`;
}

View File

@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"composite": true,
"module": "CommonJS",
"outDir": "lib",
"rootDir": "src"
},
"include": ["src"]
}