chore: use composite
builds for tests (#8522)
This commit is contained in:
parent
cba58a12c4
commit
80373f7a12
@ -1,16 +1,8 @@
|
||||
test/assets/modernizr.js
|
||||
third_party/*
|
||||
utils/doclint/check_public_api/test/
|
||||
node6/*
|
||||
node6-test/*
|
||||
experimental/
|
||||
assets/
|
||||
build/
|
||||
coverage/
|
||||
doclint/
|
||||
lib/
|
||||
/index.d.ts
|
||||
# We ignore this file because it uses ES imports which we don't yet use
|
||||
# in the Puppeteer src, so it trips up the ESLint-TypeScript parser.
|
||||
utils/doclint/generate_types/test/test.ts
|
||||
vendor/
|
||||
web-test-runner.config.mjs
|
||||
test-ts-types/
|
||||
website/
|
||||
docs-dist/
|
||||
tsconfig.tsbuildinfo
|
||||
vendor/
|
38
.gitignore
vendored
38
.gitignore
vendored
@ -1,26 +1,20 @@
|
||||
/node_modules/
|
||||
test-ts-types/**/node_modules
|
||||
test-ts-types/**/dist/
|
||||
/test/output-chromium
|
||||
/test/output-firefox
|
||||
/test/test-user-data-dir*
|
||||
.DS_Store
|
||||
.vscode
|
||||
/.dev_profile*
|
||||
/.local-chromium/
|
||||
/.local-firefox/
|
||||
/.dev_profile*
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.pyc
|
||||
.vscode
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
/node6
|
||||
lib
|
||||
test/coverage.json
|
||||
temp/
|
||||
new-docs/
|
||||
puppeteer*.tgz
|
||||
docs-api-json/
|
||||
docs-dist/
|
||||
/test/test-user-data-dir*
|
||||
build/
|
||||
coverage
|
||||
website/docs
|
||||
coverage/
|
||||
doclint/
|
||||
docs-api-json/
|
||||
docs/api.html
|
||||
lib/
|
||||
node_modules/
|
||||
package-lock.json
|
||||
puppeteer*.tgz
|
||||
test-ts-types/**/dist/
|
||||
test-ts-types/**/node_modules
|
||||
tsconfig.tsbuildinfo
|
||||
yarn.lock
|
@ -14,17 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const base = require('./base.js');
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
require: [
|
||||
'./test/mocha-ts-require',
|
||||
'./test/mocha-utils.ts',
|
||||
'source-map-support/register',
|
||||
],
|
||||
spec: 'test/*.spec.ts',
|
||||
extension: ['js', 'ts'],
|
||||
reporter: 'dot',
|
||||
logLevel: 'debug',
|
||||
require: ['./test/build/mocha-utils.js', 'source-map-support/register'],
|
||||
spec: 'test/build/*.spec.js',
|
||||
exit: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
parallel: !!process.env.PARALLEL,
|
||||
timeout: 25 * 1000,
|
@ -1,17 +1,17 @@
|
||||
node_modules/
|
||||
lib/
|
||||
third_party/
|
||||
vendor/
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
package.json
|
||||
docs-api-json/
|
||||
docs-dist/
|
||||
website/
|
||||
experimental/
|
||||
.local-chromium/
|
||||
.local-firefox/
|
||||
assets/
|
||||
build/
|
||||
CHANGELOG.md
|
||||
coverage/
|
||||
doclint/
|
||||
docs-api-json/
|
||||
lib/
|
||||
node_modules/
|
||||
package-lock.json
|
||||
package.json
|
||||
test-ts-types/
|
||||
test/assets/
|
||||
/.local-chromium/
|
||||
/.local-firefox/
|
||||
test-ts-types
|
||||
tsconfig.tsbuildinfo
|
||||
vendor/
|
||||
yarn.lock
|
@ -1,3 +1,8 @@
|
||||
{
|
||||
"extends": "../../src/tsconfig.cjs.json"
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"outDir": "../../lib/cjs/puppeteer",
|
||||
"module": "CommonJS"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
{
|
||||
"extends": "../../src/tsconfig.esm.json"
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"outDir": "../../lib/esm/puppeteer",
|
||||
"module": "esnext"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
reporter: 'dot',
|
||||
// Allow `console.log`s to show up during test execution
|
||||
logLevel: 'debug',
|
||||
exit: !!process.env.CI,
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
const base = require('./base.js');
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
spec: 'utils/doclint/**/*.spec.js',
|
||||
};
|
15
package.json
15
package.json
@ -27,15 +27,15 @@
|
||||
"node": ">=14.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm run build && npm run lint --silent && npm run test:unit:coverage",
|
||||
"test:unit": "npm run build:tsc:cjs && npm run build:tsc:cjs:compat && mocha --config mocha-config/puppeteer-unit-tests.js",
|
||||
"test": "npm run lint --silent && npm run test:unit:coverage",
|
||||
"test:unit": "npm run build:test && mocha",
|
||||
"test:unit:firefox": "cross-env PUPPETEER_PRODUCT=firefox npm run test:unit",
|
||||
"test:unit:coverage": "c8 --check-coverage --lines 94 npm run test:unit",
|
||||
"test:unit:chrome-headless": "cross-env HEADLESS=chrome npm run test:unit",
|
||||
"test:protocol-revision": "ts-node -s scripts/ensure-correct-devtools-protocol-package",
|
||||
"test:pinned-deps": "ts-node -s scripts/ensure-pinned-deps",
|
||||
"test:install": "scripts/test-install.sh",
|
||||
"test:debug": "npm run build:tsc:cjs && npm run build:tsc:cjs:compat && mocha --inspect-brk --config mocha-config/puppeteer-unit-tests.js",
|
||||
"test:debug": "npm run build:test && mocha --inspect-brk",
|
||||
"test:types": "ts-node -s scripts/test-ts-definition-files.ts",
|
||||
"prepublishOnly": "npm run build",
|
||||
"prepare": "node typescript-if-required.js && ([[ $HUSKY = 0 ]] || husky install)",
|
||||
@ -54,11 +54,10 @@
|
||||
"clean:lib": "rimraf lib",
|
||||
"clean:docs": "rimraf docs-api-json",
|
||||
"build": "npm run build:tsc && npm run generate:types && npm run generate:esm-package-json",
|
||||
"build:tsc": "npm run clean:lib && tsc --version && (npm run build:tsc:cjs && npm run build:tsc:esm) && (npm run build:tsc:cjs:compat && npm run build:tsc:esm:compat)",
|
||||
"build:test": "tsc -b test",
|
||||
"build:tsc": "npm run clean:lib && tsc --version && (npm run build:tsc:cjs && npm run build:tsc:esm)",
|
||||
"build:tsc:esm": "tsc -b src/tsconfig.esm.json",
|
||||
"build:tsc:esm:compat": "tsc -b compat/esm/tsconfig.json",
|
||||
"build:tsc:cjs": "tsc -b src/tsconfig.cjs.json",
|
||||
"build:tsc:cjs:compat": "tsc -b compat/cjs/tsconfig.json"
|
||||
"build:tsc:cjs": "tsc -b src/tsconfig.cjs.json"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
@ -104,6 +103,7 @@
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.16.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-formatter-codeframe": "7.32.1",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-mocha": "10.0.5",
|
||||
"eslint-plugin-prettier": "4.0.0",
|
||||
@ -122,7 +122,6 @@
|
||||
"sinon": "14.0.0",
|
||||
"source-map-support": "0.5.21",
|
||||
"text-diff": "1.0.1",
|
||||
"ts-node": "10.8.0",
|
||||
"typescript": "4.7.2"
|
||||
}
|
||||
}
|
||||
|
0
src/compat.ts → src/compat.d.ts
vendored
0
src/compat.ts → src/compat.d.ts
vendored
@ -5,5 +5,8 @@
|
||||
"outDir": "../lib/cjs/puppeteer",
|
||||
"module": "CommonJS"
|
||||
},
|
||||
"references": [{ "path": "../vendor/tsconfig.cjs.json" }]
|
||||
"references": [
|
||||
{ "path": "../vendor/tsconfig.cjs.json" },
|
||||
{ "path": "../compat/cjs/tsconfig.json" }
|
||||
]
|
||||
}
|
||||
|
@ -5,5 +5,8 @@
|
||||
"outDir": "../lib/esm/puppeteer",
|
||||
"module": "esnext"
|
||||
},
|
||||
"references": [{ "path": "../vendor/tsconfig.esm.json" }]
|
||||
"references": [
|
||||
{ "path": "../vendor/tsconfig.esm.json" },
|
||||
{ "path": "../compat/esm/tsconfig.json" }
|
||||
]
|
||||
}
|
||||
|
@ -1,167 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// TODO (@jackfranklin): convert this to TypeScript and enable type-checking
|
||||
// @ts-nocheck
|
||||
|
||||
/* We want to ensure that all of Puppeteer's public API is tested via our unit
|
||||
* tests but we can't use a tool like Istanbul because the way it instruments
|
||||
* code unfortunately breaks in Puppeteer where some of that code is then being
|
||||
* executed in a browser context.
|
||||
*
|
||||
* So instead we maintain this coverage code which does the following:
|
||||
* * takes every public method that we expect to be tested
|
||||
* * replaces it with a method that calls the original but also updates a Map of calls
|
||||
* * in an after() test callback it asserts that every public method was called.
|
||||
*
|
||||
* We run this when COVERAGE=1.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* This object is also used by DocLint to know which classes to check are
|
||||
* documented. It's a pretty hacky solution but DocLint is going away soon as
|
||||
* part of the TSDoc migration.
|
||||
*/
|
||||
const MODULES_TO_CHECK_FOR_COVERAGE = {
|
||||
Accessibility: '../lib/cjs/puppeteer/common/Accessibility',
|
||||
Browser: '../lib/cjs/puppeteer/common/Browser',
|
||||
BrowserContext: '../lib/cjs/puppeteer/common/Browser',
|
||||
BrowserFetcher: '../lib/cjs/puppeteer/node/BrowserFetcher',
|
||||
CDPSession: '../lib/cjs/puppeteer/common/Connection',
|
||||
ConsoleMessage: '../lib/cjs/puppeteer/common/ConsoleMessage',
|
||||
Coverage: '../lib/cjs/puppeteer/common/Coverage',
|
||||
Dialog: '../lib/cjs/puppeteer/common/Dialog',
|
||||
ElementHandle: '../lib/cjs/puppeteer/common/JSHandle',
|
||||
ExecutionContext: '../lib/cjs/puppeteer/common/ExecutionContext',
|
||||
EventEmitter: '../lib/cjs/puppeteer/common/EventEmitter',
|
||||
FileChooser: '../lib/cjs/puppeteer/common/FileChooser',
|
||||
Frame: '../lib/cjs/puppeteer/common/FrameManager',
|
||||
JSHandle: '../lib/cjs/puppeteer/common/JSHandle',
|
||||
Keyboard: '../lib/cjs/puppeteer/common/Input',
|
||||
Mouse: '../lib/cjs/puppeteer/common/Input',
|
||||
Page: '../lib/cjs/puppeteer/common/Page',
|
||||
Puppeteer: '../lib/cjs/puppeteer/common/Puppeteer',
|
||||
PuppeteerNode: '../lib/cjs/puppeteer/node/Puppeteer',
|
||||
HTTPRequest: '../lib/cjs/puppeteer/common/HTTPRequest',
|
||||
HTTPResponse: '../lib/cjs/puppeteer/common/HTTPResponse',
|
||||
SecurityDetails: '../lib/cjs/puppeteer/common/SecurityDetails',
|
||||
Target: '../lib/cjs/puppeteer/common/Target',
|
||||
TimeoutError: '../lib/cjs/puppeteer/common/Errors',
|
||||
Touchscreen: '../lib/cjs/puppeteer/common/Input',
|
||||
Tracing: '../lib/cjs/puppeteer/common/Tracing',
|
||||
WebWorker: '../lib/cjs/puppeteer/common/WebWorker',
|
||||
};
|
||||
|
||||
function traceAPICoverage(apiCoverage, className, modulePath) {
|
||||
const loadedModule = require(modulePath);
|
||||
const classType = loadedModule[className];
|
||||
|
||||
if (!classType || !classType.prototype) {
|
||||
console.error(
|
||||
`Coverage error: could not find class for ${className}. Is src/api.ts up to date?`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
for (const methodName of Reflect.ownKeys(classType.prototype)) {
|
||||
const method = Reflect.get(classType.prototype, methodName);
|
||||
if (
|
||||
methodName === 'constructor' ||
|
||||
typeof methodName !== 'string' ||
|
||||
methodName.startsWith('_') ||
|
||||
typeof method !== 'function'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
apiCoverage.set(`${className}.${methodName}`, false);
|
||||
Reflect.set(classType.prototype, methodName, function (...args) {
|
||||
apiCoverage.set(`${className}.${methodName}`, true);
|
||||
return method.call(this, ...args);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If classes emit events, those events are exposed via an object in the same
|
||||
* module named XEmittedEvents, where X is the name of the class. For example,
|
||||
* the Page module exposes PageEmittedEvents.
|
||||
*/
|
||||
const eventsName = `${className}EmittedEvents`;
|
||||
if (loadedModule[eventsName]) {
|
||||
for (const event of Object.values(loadedModule[eventsName])) {
|
||||
if (typeof event !== 'symbol') {
|
||||
apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, false);
|
||||
}
|
||||
}
|
||||
const method = Reflect.get(classType.prototype, 'emit');
|
||||
Reflect.set(classType.prototype, 'emit', function (event, ...args) {
|
||||
if (typeof event !== 'symbol' && this.listenerCount(event)) {
|
||||
apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, true);
|
||||
}
|
||||
return method.call(this, event, ...args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const coverageLocation = path.join(__dirname, 'coverage.json');
|
||||
|
||||
const clearOldCoverage = () => {
|
||||
try {
|
||||
fs.unlinkSync(coverageLocation);
|
||||
} catch (error) {
|
||||
// do nothing, the file didn't exist
|
||||
}
|
||||
};
|
||||
const writeCoverage = (coverage) => {
|
||||
fs.writeFileSync(coverageLocation, JSON.stringify([...coverage.entries()]));
|
||||
};
|
||||
|
||||
const getCoverageResults = () => {
|
||||
let contents;
|
||||
try {
|
||||
contents = fs.readFileSync(coverageLocation, { encoding: 'utf8' });
|
||||
} catch (error) {
|
||||
console.error('Warning: coverage file does not exist or is not readable.');
|
||||
}
|
||||
|
||||
const coverageMap = new Map(JSON.parse(contents));
|
||||
return coverageMap;
|
||||
};
|
||||
|
||||
const trackCoverage = () => {
|
||||
clearOldCoverage();
|
||||
const coverageMap = new Map();
|
||||
|
||||
return {
|
||||
beforeAll: () => {
|
||||
for (const [className, moduleFilePath] of Object.entries(
|
||||
MODULES_TO_CHECK_FOR_COVERAGE
|
||||
)) {
|
||||
traceAPICoverage(coverageMap, className, moduleFilePath);
|
||||
}
|
||||
},
|
||||
afterAll: () => {
|
||||
writeCoverage(coverageMap);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
trackCoverage,
|
||||
getCoverageResults,
|
||||
MODULES_TO_CHECK_FOR_COVERAGE,
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
const path = require('path');
|
||||
|
||||
require('ts-node').register({
|
||||
/**
|
||||
* We ignore the lib/ directory because that's already been TypeScript
|
||||
* compiled and checked. So we don't want to check it again as part of running
|
||||
* the unit tests.
|
||||
*/
|
||||
ignore: ['lib/*', 'node_modules'],
|
||||
project: path.join(__dirname, 'tsconfig.test.json'),
|
||||
});
|
@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const path = require('path');
|
||||
const { TestServer } = require('../utils/testserver/index.js');
|
||||
|
||||
const port = 8907;
|
||||
const httpsPort = 8908;
|
||||
const assetsPath = path.join(__dirname, 'assets');
|
||||
const cachedPath = path.join(__dirname, 'assets', 'cached');
|
||||
|
||||
Promise.all([
|
||||
TestServer.create(assetsPath, port),
|
||||
TestServer.createHTTPS(assetsPath, httpsPort),
|
||||
]).then(([server, httpsServer]) => {
|
||||
server.enableHTTPCache(cachedPath);
|
||||
httpsServer.enableHTTPCache(cachedPath);
|
||||
console.log(`HTTP: server is running on http://localhost:${port}`);
|
||||
console.log(`HTTPS: server is running on https://localhost:${httpsPort}`);
|
||||
});
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from '../lib/cjs/puppeteer/common/EventEmitter.js';
|
||||
import { EventEmitter } from '../../lib/cjs/puppeteer/common/EventEmitter.js';
|
||||
import sinon from 'sinon';
|
||||
import expect from 'expect';
|
||||
|
@ -20,10 +20,10 @@ import expect from 'expect';
|
||||
import {
|
||||
NetworkManager,
|
||||
NetworkManagerEmittedEvents,
|
||||
} from '../lib/cjs/puppeteer/common/NetworkManager.js';
|
||||
import { HTTPRequest } from '../lib/cjs/puppeteer/common/HTTPRequest.js';
|
||||
import { EventEmitter } from '../lib/cjs/puppeteer/common/EventEmitter.js';
|
||||
import { Frame } from '../lib/cjs/puppeteer/common/FrameManager.js';
|
||||
} from '../../lib/cjs/puppeteer/common/NetworkManager.js';
|
||||
import { HTTPRequest } from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
|
||||
import { EventEmitter } from '../../lib/cjs/puppeteer/common/EventEmitter.js';
|
||||
import { Frame } from '../../lib/cjs/puppeteer/common/FrameManager.js';
|
||||
|
||||
class MockCDPSession extends EventEmitter {
|
||||
async send(): Promise<any> {}
|
@ -22,7 +22,7 @@ import {
|
||||
describeChromeOnly,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
|
||||
import { ElementHandle } from '../lib/cjs/puppeteer/common/JSHandle.js';
|
||||
import { ElementHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
||||
import utils from './utils.js';
|
||||
|
||||
describeChromeOnly('AriaQueryHandler', () => {
|
@ -25,7 +25,7 @@ import {
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
|
||||
import utils from './utils.js';
|
||||
import { ElementHandle } from '../lib/cjs/puppeteer/common/JSHandle.js';
|
||||
import { ElementHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
||||
|
||||
describe('ElementHandle specs', function () {
|
||||
setupTestBrowserHooks();
|
@ -32,7 +32,7 @@ describe('Fixtures', function () {
|
||||
dumpio: true,
|
||||
});
|
||||
const res = spawn('node', [
|
||||
path.join(__dirname, 'fixtures', 'dumpio.js'),
|
||||
path.join(__dirname, '../fixtures', 'dumpio.js'),
|
||||
puppeteerPath,
|
||||
JSON.stringify(options),
|
||||
]);
|
||||
@ -47,7 +47,7 @@ describe('Fixtures', function () {
|
||||
const { spawn } = await import('child_process');
|
||||
const options = Object.assign({}, defaultBrowserOptions, { dumpio: true });
|
||||
const res = spawn('node', [
|
||||
path.join(__dirname, 'fixtures', 'dumpio.js'),
|
||||
path.join(__dirname, '../fixtures', 'dumpio.js'),
|
||||
puppeteerPath,
|
||||
JSON.stringify(options),
|
||||
]);
|
||||
@ -64,7 +64,7 @@ describe('Fixtures', function () {
|
||||
dumpio: false,
|
||||
});
|
||||
const res = spawn('node', [
|
||||
path.join(__dirname, 'fixtures', 'closeme.js'),
|
||||
path.join(__dirname, '../fixtures', 'closeme.js'),
|
||||
puppeteerPath,
|
||||
JSON.stringify(options),
|
||||
]);
|
@ -22,7 +22,7 @@ import {
|
||||
setupTestPageAndContextHooks,
|
||||
itFailsFirefox,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
import { CDPSession } from '../lib/cjs/puppeteer/common/Connection.js';
|
||||
import { CDPSession } from '../../lib/cjs/puppeteer/common/Connection.js';
|
||||
|
||||
describe('Frame specs', function () {
|
||||
setupTestBrowserHooks();
|
@ -31,7 +31,7 @@ const mkdtempAsync = promisify(fs.mkdtemp);
|
||||
|
||||
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
|
||||
|
||||
const extensionPath = path.join(__dirname, 'assets', 'simple-extension');
|
||||
const extensionPath = path.join(__dirname, '../assets', 'simple-extension');
|
||||
|
||||
describeChromeOnly('headful tests', function () {
|
||||
/* These tests fire up an actual browser so let's
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { TLSSocket } from 'tls';
|
||||
import {
|
||||
getTestState,
|
||||
describeFailsFirefox,
|
||||
@ -65,7 +66,9 @@ describe('ignoreHTTPSErrors', function () {
|
||||
]);
|
||||
const securityDetails = response.securityDetails();
|
||||
expect(securityDetails.issuer()).toBe('puppeteer-tests');
|
||||
const protocol = serverRequest.socket.getProtocol().replace('v', ' ');
|
||||
const protocol = (serverRequest.socket as TLSSocket)
|
||||
.getProtocol()
|
||||
.replace('v', ' ');
|
||||
expect(securityDetails.protocol()).toBe(protocol);
|
||||
expect(securityDetails.subjectName()).toBe('puppeteer-tests');
|
||||
expect(securityDetails.validFrom()).toBe(1589357069);
|
||||
@ -94,7 +97,9 @@ describe('ignoreHTTPSErrors', function () {
|
||||
expect(responses.length).toBe(2);
|
||||
expect(responses[0].status()).toBe(302);
|
||||
const securityDetails = responses[0].securityDetails();
|
||||
const protocol = serverRequest.socket.getProtocol().replace('v', ' ');
|
||||
const protocol = (serverRequest.socket as TLSSocket)
|
||||
.getProtocol()
|
||||
.replace('v', ' ');
|
||||
expect(securityDetails.protocol()).toBe(protocol);
|
||||
});
|
||||
});
|
@ -23,7 +23,7 @@ import {
|
||||
describeFailsFirefox,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
|
||||
const FILE_TO_UPLOAD = path.join(__dirname, '/assets/file-to-upload.txt');
|
||||
const FILE_TO_UPLOAD = path.join(__dirname, '/../assets/file-to-upload.txt');
|
||||
|
||||
describe('input tests', function () {
|
||||
setupTestBrowserHooks();
|
||||
@ -228,9 +228,9 @@ describe('input tests', function () {
|
||||
.accept([
|
||||
path.relative(
|
||||
process.cwd(),
|
||||
__dirname + '/assets/file-to-upload.txt'
|
||||
__dirname + '/../assets/file-to-upload.txt'
|
||||
),
|
||||
path.relative(process.cwd(), __dirname + '/assets/pptr.png'),
|
||||
path.relative(process.cwd(), __dirname + '/../assets/pptr.png'),
|
||||
])
|
||||
.catch((error_) => (error = error_));
|
||||
expect(error).not.toBe(null);
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { JSHandle } from '../lib/cjs/puppeteer/common/JSHandle.js';
|
||||
import { JSHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
||||
import {
|
||||
getTestState,
|
||||
setupTestBrowserHooks,
|
@ -23,7 +23,7 @@ import {
|
||||
setupTestPageAndContextHooks,
|
||||
itFailsFirefox,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
import { KeyInput } from '../lib/cjs/puppeteer/common/USKeyboardLayout.js';
|
||||
import { KeyInput } from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js';
|
||||
|
||||
describe('Keyboard', function () {
|
||||
setupTestBrowserHooks();
|
@ -29,7 +29,8 @@ import {
|
||||
import utils from './utils.js';
|
||||
import expect from 'expect';
|
||||
import rimraf from 'rimraf';
|
||||
import { Page } from '../lib/cjs/puppeteer/common/Page.js';
|
||||
import { Page } from '../../lib/cjs/puppeteer/common/Page.js';
|
||||
import { TLSSocket } from 'tls';
|
||||
|
||||
const mkdtempAsync = promisify(fs.mkdtemp);
|
||||
const readFileAsync = promisify(fs.readFile);
|
||||
@ -677,7 +678,9 @@ describe('Launcher specs', function () {
|
||||
expect(error).toBe(null);
|
||||
expect(response.ok()).toBe(true);
|
||||
expect(response.securityDetails()).toBeTruthy();
|
||||
const protocol = serverRequest.socket.getProtocol().replace('v', ' ');
|
||||
const protocol = (serverRequest.socket as TLSSocket)
|
||||
.getProtocol()
|
||||
.replace('v', ' ');
|
||||
expect(response.securityDetails().protocol()).toBe(protocol);
|
||||
await page.close();
|
||||
await browser.close();
|
@ -24,17 +24,17 @@ import sinon from 'sinon';
|
||||
import {
|
||||
Browser,
|
||||
BrowserContext,
|
||||
} from '../lib/cjs/puppeteer/common/Browser.js';
|
||||
import { isErrorLike } from '../lib/cjs/puppeteer/common/util.js';
|
||||
import { Page } from '../lib/cjs/puppeteer/common/Page.js';
|
||||
import { PuppeteerNode } from '../lib/cjs/puppeteer/node/Puppeteer.js';
|
||||
import puppeteer from '../lib/cjs/puppeteer/puppeteer.js';
|
||||
import { TestServer } from '../utils/testserver/index.js';
|
||||
} from '../../lib/cjs/puppeteer/common/Browser.js';
|
||||
import { isErrorLike } from '../../lib/cjs/puppeteer/common/util.js';
|
||||
import { Page } from '../../lib/cjs/puppeteer/common/Page.js';
|
||||
import { PuppeteerNode } from '../../lib/cjs/puppeteer/node/Puppeteer.js';
|
||||
import puppeteer from '../../lib/cjs/puppeteer/puppeteer.js';
|
||||
import utils from './utils.js';
|
||||
import { TestServer } from '../../utils/testserver/lib/index.js';
|
||||
|
||||
const setupServer = async () => {
|
||||
const assetsPath = path.join(__dirname, 'assets');
|
||||
const cachedPath = path.join(__dirname, 'assets', 'cached');
|
||||
const assetsPath = path.join(__dirname, '../assets');
|
||||
const cachedPath = path.join(__dirname, '../assets', 'cached');
|
||||
|
||||
const port = 8907;
|
||||
const server = await TestServer.create(assetsPath, port);
|
||||
@ -122,8 +122,8 @@ declare module 'expect/build/types' {
|
||||
|
||||
const setupGoldenAssertions = (): void => {
|
||||
const suffix = product.toLowerCase();
|
||||
const GOLDEN_DIR = path.join(__dirname, 'golden-' + suffix);
|
||||
const OUTPUT_DIR = path.join(__dirname, 'output-' + suffix);
|
||||
const GOLDEN_DIR = path.join(__dirname, `../golden-${suffix}`);
|
||||
const OUTPUT_DIR = path.join(__dirname, `../output-${suffix}`);
|
||||
if (fs.existsSync(OUTPUT_DIR)) {
|
||||
rimraf.sync(OUTPUT_DIR);
|
||||
}
|
||||
@ -140,8 +140,8 @@ interface PuppeteerTestState {
|
||||
defaultBrowserOptions: {
|
||||
[x: string]: any;
|
||||
};
|
||||
server: any;
|
||||
httpsServer: any;
|
||||
server: TestServer;
|
||||
httpsServer: TestServer;
|
||||
isFirefox: boolean;
|
||||
isChrome: boolean;
|
||||
isHeadless: boolean;
|
||||
@ -299,7 +299,7 @@ export const mochaHooks = {
|
||||
state.isChrome = isChrome;
|
||||
state.isHeadless = isHeadless;
|
||||
state.headless = headless;
|
||||
state.puppeteerPath = path.resolve(path.join(__dirname, '..'));
|
||||
state.puppeteerPath = path.resolve(path.join(__dirname, '../..'));
|
||||
},
|
||||
],
|
||||
|
@ -21,7 +21,7 @@ import {
|
||||
setupTestPageAndContextHooks,
|
||||
itFailsFirefox,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
import { KeyInput } from '../lib/cjs/puppeteer/common/USKeyboardLayout.js';
|
||||
import { KeyInput } from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js';
|
||||
|
||||
interface Dimensions {
|
||||
x: number;
|
@ -27,7 +27,7 @@ import {
|
||||
itChromeOnly,
|
||||
itFirefoxOnly,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
import { HTTPResponse } from '../lib/cjs/puppeteer/api-docs-entry.js';
|
||||
import { HTTPResponse } from '../../lib/cjs/puppeteer/api-docs-entry.js';
|
||||
|
||||
describe('network', function () {
|
||||
setupTestBrowserHooks();
|
||||
@ -363,7 +363,7 @@ describe('network', function () {
|
||||
|
||||
const response = await page.goto(server.PREFIX + '/pptr.png');
|
||||
const imageBuffer = fs.readFileSync(
|
||||
path.join(__dirname, 'assets', 'pptr.png')
|
||||
path.join(__dirname, '../assets', 'pptr.png')
|
||||
);
|
||||
const responseBuffer = await response.buffer();
|
||||
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
||||
@ -374,7 +374,7 @@ describe('network', function () {
|
||||
server.enableGzip('/pptr.png');
|
||||
const response = await page.goto(server.PREFIX + '/pptr.png');
|
||||
const imageBuffer = fs.readFileSync(
|
||||
path.join(__dirname, 'assets', 'pptr.png')
|
||||
path.join(__dirname, '../assets', 'pptr.png')
|
||||
);
|
||||
const responseBuffer = await response.buffer();
|
||||
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
@ -25,7 +25,7 @@ import {
|
||||
Browser,
|
||||
BrowserContext,
|
||||
Page,
|
||||
} from '../lib/cjs/puppeteer/api-docs-entry.js';
|
||||
} from '../../lib/cjs/puppeteer/api-docs-entry.js';
|
||||
|
||||
describeChromeOnly('OOPIF', function () {
|
||||
/* We use a special browser for this test as we need the --site-per-process flag */
|
@ -26,9 +26,9 @@ import {
|
||||
itFailsFirefox,
|
||||
describeFailsFirefox,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
import { Page, Metrics } from '../lib/cjs/puppeteer/common/Page.js';
|
||||
import { CDPSession } from '../lib/cjs/puppeteer/common/Connection.js';
|
||||
import { JSHandle } from '../lib/cjs/puppeteer/common/JSHandle.js';
|
||||
import { Page, Metrics } from '../../lib/cjs/puppeteer/common/Page.js';
|
||||
import { CDPSession } from '../../lib/cjs/puppeteer/common/Connection.js';
|
||||
import { JSHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
||||
|
||||
describe('Page', function () {
|
||||
setupTestBrowserHooks();
|
||||
@ -967,7 +967,7 @@ describe('Page', function () {
|
||||
it('should throw exception in page context', async () => {
|
||||
const { page } = getTestState();
|
||||
|
||||
await page.exposeFunction('woof', function () {
|
||||
await page.exposeFunction('woof', () => {
|
||||
throw new Error('WOOF WOOF');
|
||||
});
|
||||
const { message, stack } = await page.evaluate(async () => {
|
||||
@ -978,7 +978,7 @@ describe('Page', function () {
|
||||
}
|
||||
});
|
||||
expect(message).toBe('WOOF WOOF');
|
||||
expect(stack).toContain(__filename);
|
||||
expect(stack).toContain('page.spec.ts');
|
||||
});
|
||||
it('should support throwing "null"', async () => {
|
||||
const { page } = getTestState();
|
||||
@ -1425,7 +1425,7 @@ describe('Page', function () {
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({
|
||||
path: path.join(__dirname, 'assets/es6/es6pathimport.js'),
|
||||
path: path.join(__dirname, '../assets/es6/es6pathimport.js'),
|
||||
type: 'module',
|
||||
});
|
||||
await page.waitForFunction('window.__es6injected');
|
||||
@ -1462,7 +1462,7 @@ describe('Page', function () {
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const scriptHandle = await page.addScriptTag({
|
||||
path: path.join(__dirname, 'assets/injectedfile.js'),
|
||||
path: path.join(__dirname, '../assets/injectedfile.js'),
|
||||
});
|
||||
expect(scriptHandle.asElement()).not.toBeNull();
|
||||
expect(await page.evaluate(() => globalThis.__injected)).toBe(42);
|
||||
@ -1473,7 +1473,7 @@ describe('Page', function () {
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({
|
||||
path: path.join(__dirname, 'assets/injectedfile.js'),
|
||||
path: path.join(__dirname, '../assets/injectedfile.js'),
|
||||
});
|
||||
const result = await page.evaluate(
|
||||
() => globalThis.__injectedError.stack
|
||||
@ -1572,7 +1572,7 @@ describe('Page', function () {
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const styleHandle = await page.addStyleTag({
|
||||
path: path.join(__dirname, 'assets/injectedstyle.css'),
|
||||
path: path.join(__dirname, '../assets/injectedstyle.css'),
|
||||
});
|
||||
expect(styleHandle.asElement()).not.toBeNull();
|
||||
expect(
|
||||
@ -1587,7 +1587,7 @@ describe('Page', function () {
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addStyleTag({
|
||||
path: path.join(__dirname, 'assets/injectedstyle.css'),
|
||||
path: path.join(__dirname, '../assets/injectedstyle.css'),
|
||||
});
|
||||
const styleHandle = await page.$('style');
|
||||
const styleContent = await page.evaluate(
|
||||
@ -1717,7 +1717,7 @@ describe('Page', function () {
|
||||
return;
|
||||
}
|
||||
|
||||
const outputFile = __dirname + '/assets/output.pdf';
|
||||
const outputFile = __dirname + '/../assets/output.pdf';
|
||||
await page.pdf({ path: outputFile });
|
||||
expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0);
|
||||
fs.unlinkSync(outputFile);
|
@ -23,8 +23,9 @@ import {
|
||||
itFailsWindows,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
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 { TestServer } from '../../utils/testserver/lib/index.js';
|
||||
|
||||
const HOSTNAME = os.hostname().toLowerCase();
|
||||
|
||||
@ -32,7 +33,7 @@ const HOSTNAME = os.hostname().toLowerCase();
|
||||
* Requests to localhost do not get proxied by default. Create a URL using the hostname
|
||||
* instead.
|
||||
*/
|
||||
function getEmptyPageUrl(server: { PORT: number; EMPTY_PAGE: string }): string {
|
||||
function getEmptyPageUrl(server: TestServer): string {
|
||||
const emptyPagePath = new URL(server.EMPTY_PAGE).pathname;
|
||||
|
||||
return `http://${HOSTNAME}:${server.PORT}${emptyPagePath}`;
|
@ -19,7 +19,7 @@ import {
|
||||
setupTestBrowserHooks,
|
||||
setupTestPageAndContextHooks,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
import { CustomQueryHandler } from '../lib/cjs/puppeteer/common/QueryHandler.js';
|
||||
import { CustomQueryHandler } from '../../lib/cjs/puppeteer/common/QueryHandler.js';
|
||||
|
||||
describe('querySelector', function () {
|
||||
setupTestBrowserHooks();
|
@ -24,8 +24,8 @@ import {
|
||||
setupTestPageAndContextHooks,
|
||||
describeFailsFirefox,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
import { ActionResult } from '../lib/cjs/puppeteer/api-docs-entry.js';
|
||||
import { InterceptResolutionAction } from '../lib/cjs/puppeteer/common/HTTPRequest.js';
|
||||
import { ActionResult } from '../../lib/cjs/puppeteer/api-docs-entry.js';
|
||||
import { InterceptResolutionAction } from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
|
||||
|
||||
describe('request interception', function () {
|
||||
setupTestBrowserHooks();
|
||||
@ -592,7 +592,7 @@ describe('request interception', function () {
|
||||
request.continue({}, 0);
|
||||
});
|
||||
await page.goto(
|
||||
pathToFileURL(path.join(__dirname, 'assets', 'one-style.html'))
|
||||
pathToFileURL(path.join(__dirname, '../assets', 'one-style.html'))
|
||||
);
|
||||
expect(urls.size).toBe(2);
|
||||
expect(urls.has('one-style.html')).toBe(true);
|
||||
@ -827,7 +827,7 @@ describe('request interception', function () {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', (request) => {
|
||||
const imageBuffer = fs.readFileSync(
|
||||
path.join(__dirname, 'assets', 'pptr.png')
|
||||
path.join(__dirname, '../assets', 'pptr.png')
|
||||
);
|
||||
request.respond(
|
||||
{
|
@ -507,7 +507,7 @@ describe('request interception', function () {
|
||||
request.continue();
|
||||
});
|
||||
await page.goto(
|
||||
pathToFileURL(path.join(__dirname, 'assets', 'one-style.html'))
|
||||
pathToFileURL(path.join(__dirname, '../assets', 'one-style.html'))
|
||||
);
|
||||
expect(urls.size).toBe(2);
|
||||
expect(urls.has('one-style.html')).toBe(true);
|
||||
@ -759,7 +759,7 @@ describe('request interception', function () {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', (request) => {
|
||||
const imageBuffer = fs.readFileSync(
|
||||
path.join(__dirname, 'assets', 'pptr.png')
|
||||
path.join(__dirname, '../assets', 'pptr.png')
|
||||
);
|
||||
request.respond({
|
||||
contentType: 'image/png',
|
@ -23,7 +23,7 @@ import {
|
||||
setupTestPageAndContextHooks,
|
||||
itFailsFirefox,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
import { Target } from '../lib/cjs/puppeteer/common/Target.js';
|
||||
import { Target } from '../../lib/cjs/puppeteer/common/Target.js';
|
||||
|
||||
describe('Target', function () {
|
||||
setupTestBrowserHooks();
|
@ -32,7 +32,7 @@ describeChromeOnly('Tracing', function () {
|
||||
const { defaultBrowserOptions, puppeteer } = getTestState();
|
||||
browser = await puppeteer.launch(defaultBrowserOptions);
|
||||
page = await browser.newPage();
|
||||
outputFile = path.join(__dirname, 'assets', 'trace.json');
|
||||
outputFile = path.join(__dirname, 'trace.json');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
@ -17,13 +17,10 @@
|
||||
// TODO (@jackfranklin): convert to TS and enable type checking.
|
||||
|
||||
// @ts-nocheck
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const expect = require('expect');
|
||||
const GoldenUtils = require('./golden-utils.js');
|
||||
const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json'))
|
||||
? path.join(__dirname, '..')
|
||||
: path.join(__dirname, '..', '..');
|
||||
const PROJECT_ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
const utils = (module.exports = {
|
||||
extendExpectWithToBeGolden: function (goldenDir, outputDir) {
|
@ -22,8 +22,8 @@ import {
|
||||
describeFailsFirefox,
|
||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||
import utils from './utils.js';
|
||||
import { WebWorker } from '../lib/cjs/puppeteer/common/WebWorker.js';
|
||||
import { ConsoleMessage } from '../lib/cjs/puppeteer/common/ConsoleMessage.js';
|
||||
import { WebWorker } from '../../lib/cjs/puppeteer/common/WebWorker.js';
|
||||
import { ConsoleMessage } from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
|
||||
const { waitEvent } = utils;
|
||||
|
||||
describeFailsFirefox('Workers', function () {
|
@ -1,6 +1,21 @@
|
||||
{
|
||||
"extends": "./tsconfig.test.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true
|
||||
}
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"target": "esnext",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "build"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{ "path": "../src/tsconfig.cjs.json" },
|
||||
{ "path": "../utils/testserver/tsconfig.json" }
|
||||
]
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"target": "esnext",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true
|
||||
}
|
||||
}
|
@ -18,9 +18,36 @@ const jsBuilder = require('./JSBuilder.js');
|
||||
const mdBuilder = require('./MDBuilder.js');
|
||||
const Documentation = require('./Documentation.js');
|
||||
const Message = require('../Message.js');
|
||||
const {
|
||||
MODULES_TO_CHECK_FOR_COVERAGE,
|
||||
} = require('../../../test/coverage-utils.js');
|
||||
|
||||
const MODULES_TO_CHECK_FOR_COVERAGE = {
|
||||
Accessibility: '../../lib/cjs/puppeteer/common/Accessibility',
|
||||
Browser: '../../lib/cjs/puppeteer/common/Browser',
|
||||
BrowserContext: '../../lib/cjs/puppeteer/common/Browser',
|
||||
BrowserFetcher: '../../lib/cjs/puppeteer/node/BrowserFetcher',
|
||||
CDPSession: '../../lib/cjs/puppeteer/common/Connection',
|
||||
ConsoleMessage: '../../lib/cjs/puppeteer/common/ConsoleMessage',
|
||||
Coverage: '../../lib/cjs/puppeteer/common/Coverage',
|
||||
Dialog: '../../lib/cjs/puppeteer/common/Dialog',
|
||||
ElementHandle: '../../lib/cjs/puppeteer/common/JSHandle',
|
||||
ExecutionContext: '../../lib/cjs/puppeteer/common/ExecutionContext',
|
||||
EventEmitter: '../../lib/cjs/puppeteer/common/EventEmitter',
|
||||
FileChooser: '../../lib/cjs/puppeteer/common/FileChooser',
|
||||
Frame: '../../lib/cjs/puppeteer/common/FrameManager',
|
||||
JSHandle: '../../lib/cjs/puppeteer/common/JSHandle',
|
||||
Keyboard: '../../lib/cjs/puppeteer/common/Input',
|
||||
Mouse: '../../lib/cjs/puppeteer/common/Input',
|
||||
Page: '../../lib/cjs/puppeteer/common/Page',
|
||||
Puppeteer: '../../lib/cjs/puppeteer/common/Puppeteer',
|
||||
PuppeteerNode: '../../lib/cjs/puppeteer/node/Puppeteer',
|
||||
HTTPRequest: '../../lib/cjs/puppeteer/common/HTTPRequest',
|
||||
HTTPResponse: '../../lib/cjs/puppeteer/common/HTTPResponse',
|
||||
SecurityDetails: '../../lib/cjs/puppeteer/common/SecurityDetails',
|
||||
Target: '../../lib/cjs/puppeteer/common/Target',
|
||||
TimeoutError: '../../lib/cjs/puppeteer/common/Errors',
|
||||
Touchscreen: '../../lib/cjs/puppeteer/common/Input',
|
||||
Tracing: '../../lib/cjs/puppeteer/common/Tracing',
|
||||
WebWorker: '../../lib/cjs/puppeteer/common/WebWorker',
|
||||
};
|
||||
|
||||
const EXCLUDE_PROPERTIES = new Set([
|
||||
'Browser.create',
|
||||
|
@ -1,303 +0,0 @@
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const url = require('url');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const mime = require('mime');
|
||||
const WebSocketServer = require('ws').Server;
|
||||
|
||||
const fulfillSymbol = Symbol('fullfil callback');
|
||||
const rejectSymbol = Symbol('reject callback');
|
||||
|
||||
class TestServer {
|
||||
/** @type number */
|
||||
PORT = undefined;
|
||||
/** @type string */
|
||||
PREFIX = undefined;
|
||||
/** @type string */
|
||||
CROSS_PROCESS_PREFIX = undefined;
|
||||
/** @type string */
|
||||
EMPTY_PAGE = undefined;
|
||||
|
||||
/**
|
||||
* @param {string} dirPath
|
||||
* @param {number} port
|
||||
* @returns {!TestServer}
|
||||
*/
|
||||
static async create(dirPath, port) {
|
||||
const server = new TestServer(dirPath, port);
|
||||
await new Promise((x) => server._server.once('listening', x));
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dirPath
|
||||
* @param {number} port
|
||||
* @returns {!TestServer}
|
||||
*/
|
||||
static async createHTTPS(dirPath, port) {
|
||||
const server = new TestServer(dirPath, port, {
|
||||
key: fs.readFileSync(path.join(__dirname, 'key.pem')),
|
||||
cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
|
||||
passphrase: 'aaaa',
|
||||
});
|
||||
await new Promise((x) => server._server.once('listening', x));
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dirPath
|
||||
* @param {number} port
|
||||
* @param {!Object=} sslOptions
|
||||
*/
|
||||
constructor(dirPath, port, sslOptions) {
|
||||
if (sslOptions) {
|
||||
this._server = https.createServer(sslOptions, this._onRequest.bind(this));
|
||||
} else {
|
||||
this._server = http.createServer(this._onRequest.bind(this));
|
||||
}
|
||||
this._server.on('connection', (socket) => this._onSocket(socket));
|
||||
this._wsServer = new WebSocketServer({ server: this._server });
|
||||
this._wsServer.on('connection', this._onWebSocketConnection.bind(this));
|
||||
this._server.listen(port);
|
||||
this._dirPath = dirPath;
|
||||
|
||||
this._startTime = new Date();
|
||||
this._cachedPathPrefix = null;
|
||||
|
||||
/** @type {!Set<!net.Socket>} */
|
||||
this._sockets = new Set();
|
||||
|
||||
/** @type {!Map<string, function(!IncomingMessage, !ServerResponse)>} */
|
||||
this._routes = new Map();
|
||||
/** @type {!Map<string, !{username:string, password:string}>} */
|
||||
this._auths = new Map();
|
||||
/** @type {!Map<string, string>} */
|
||||
this._csp = new Map();
|
||||
/** @type {!Set<string>} */
|
||||
this._gzipRoutes = new Set();
|
||||
/** @type {!Map<string, !Promise>} */
|
||||
this._requestSubscribers = new Map();
|
||||
}
|
||||
|
||||
_onSocket(socket) {
|
||||
this._sockets.add(socket);
|
||||
// ECONNRESET is a legit error given
|
||||
// that tab closing simply kills process.
|
||||
socket.on('error', (error) => {
|
||||
if (error.code !== 'ECONNRESET') {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
socket.once('close', () => this._sockets.delete(socket));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} pathPrefix
|
||||
*/
|
||||
enableHTTPCache(pathPrefix) {
|
||||
this._cachedPathPrefix = pathPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
*/
|
||||
setAuth(path, username, password) {
|
||||
this._auths.set(path, { username, password });
|
||||
}
|
||||
|
||||
enableGzip(path) {
|
||||
this._gzipRoutes.add(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {string} csp
|
||||
*/
|
||||
setCSP(path, csp) {
|
||||
this._csp.set(path, csp);
|
||||
}
|
||||
|
||||
async stop() {
|
||||
this.reset();
|
||||
for (const socket of this._sockets) {
|
||||
socket.destroy();
|
||||
}
|
||||
this._sockets.clear();
|
||||
await new Promise((x) => this._server.close(x));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {function(!IncomingMessage, !ServerResponse)} handler
|
||||
*/
|
||||
setRoute(path, handler) {
|
||||
this._routes.set(path, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} from
|
||||
* @param {string} to
|
||||
*/
|
||||
setRedirect(from, to) {
|
||||
this.setRoute(from, (req, res) => {
|
||||
res.writeHead(302, { location: to });
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {!Promise<!IncomingMessage>}
|
||||
*/
|
||||
waitForRequest(path) {
|
||||
let promise = this._requestSubscribers.get(path);
|
||||
if (promise) {
|
||||
return promise;
|
||||
}
|
||||
let fulfill, reject;
|
||||
promise = new Promise((f, r) => {
|
||||
fulfill = f;
|
||||
reject = r;
|
||||
});
|
||||
promise[fulfillSymbol] = fulfill;
|
||||
promise[rejectSymbol] = reject;
|
||||
this._requestSubscribers.set(path, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._routes.clear();
|
||||
this._auths.clear();
|
||||
this._csp.clear();
|
||||
this._gzipRoutes.clear();
|
||||
const error = new Error('Static Server has been reset');
|
||||
for (const subscriber of this._requestSubscribers.values()) {
|
||||
subscriber[rejectSymbol].call(null, error);
|
||||
}
|
||||
this._requestSubscribers.clear();
|
||||
}
|
||||
|
||||
_onRequest(request, response) {
|
||||
request.on('error', (error) => {
|
||||
if (error.code === 'ECONNRESET') {
|
||||
response.end();
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
request.postBody = new Promise((resolve) => {
|
||||
let body = '';
|
||||
request.on('data', (chunk) => (body += chunk));
|
||||
request.on('end', () => resolve(body));
|
||||
});
|
||||
const pathName = url.parse(request.url).path;
|
||||
if (this._auths.has(pathName)) {
|
||||
const auth = this._auths.get(pathName);
|
||||
const credentials = Buffer.from(
|
||||
(request.headers.authorization || '').split(' ')[1] || '',
|
||||
'base64'
|
||||
).toString();
|
||||
if (credentials !== `${auth.username}:${auth.password}`) {
|
||||
response.writeHead(401, {
|
||||
'WWW-Authenticate': 'Basic realm="Secure Area"',
|
||||
});
|
||||
response.end('HTTP Error 401 Unauthorized: Access is denied');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Notify request subscriber.
|
||||
if (this._requestSubscribers.has(pathName)) {
|
||||
this._requestSubscribers.get(pathName)[fulfillSymbol].call(null, request);
|
||||
this._requestSubscribers.delete(pathName);
|
||||
}
|
||||
const handler = this._routes.get(pathName);
|
||||
if (handler) {
|
||||
handler.call(null, request, response);
|
||||
} else {
|
||||
const pathName = url.parse(request.url).path;
|
||||
this.serveFile(request, response, pathName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!IncomingMessage} request
|
||||
* @param {!ServerResponse} response
|
||||
* @param {string} pathName
|
||||
*/
|
||||
serveFile(request, response, pathName) {
|
||||
if (pathName === '/') {
|
||||
pathName = '/index.html';
|
||||
}
|
||||
const filePath = path.join(this._dirPath, pathName.substring(1));
|
||||
|
||||
if (
|
||||
this._cachedPathPrefix !== null &&
|
||||
filePath.startsWith(this._cachedPathPrefix)
|
||||
) {
|
||||
if (request.headers['if-modified-since']) {
|
||||
response.statusCode = 304; // not modified
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
response.setHeader('Cache-Control', 'public, max-age=31536000');
|
||||
response.setHeader('Last-Modified', this._startTime.toISOString());
|
||||
} else {
|
||||
response.setHeader('Cache-Control', 'no-cache, no-store');
|
||||
}
|
||||
if (this._csp.has(pathName)) {
|
||||
response.setHeader('Content-Security-Policy', this._csp.get(pathName));
|
||||
}
|
||||
|
||||
fs.readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
response.statusCode = 404;
|
||||
response.end(`File not found: ${filePath}`);
|
||||
return;
|
||||
}
|
||||
const mimeType = mime.getType(filePath);
|
||||
const isTextEncoding = /^text\/|^application\/(javascript|json)/.test(
|
||||
mimeType
|
||||
);
|
||||
const contentType = isTextEncoding
|
||||
? `${mimeType}; charset=utf-8`
|
||||
: mimeType;
|
||||
response.setHeader('Content-Type', contentType);
|
||||
if (this._gzipRoutes.has(pathName)) {
|
||||
response.setHeader('Content-Encoding', 'gzip');
|
||||
const zlib = require('zlib');
|
||||
zlib.gzip(data, (_, result) => {
|
||||
response.end(result);
|
||||
});
|
||||
} else {
|
||||
response.end(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_onWebSocketConnection(connection) {
|
||||
connection.send('opened');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { TestServer };
|
@ -2,7 +2,7 @@
|
||||
"name": "@pptr/testserver",
|
||||
"version": "0.5.0",
|
||||
"description": "testing server",
|
||||
"main": "index.js",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
|
278
utils/testserver/src/index.ts
Normal file
278
utils/testserver/src/index.ts
Normal file
@ -0,0 +1,278 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import assert from 'assert';
|
||||
import { readFile, readFileSync } from 'fs';
|
||||
import {
|
||||
createServer as createHttpServer,
|
||||
IncomingMessage,
|
||||
RequestListener,
|
||||
Server as HttpServer,
|
||||
ServerResponse,
|
||||
} from 'http';
|
||||
import {
|
||||
createServer as createHttpsServer,
|
||||
Server as HttpsServer,
|
||||
ServerOptions as HttpsServerOptions,
|
||||
} from 'https';
|
||||
import { getType as getMimeType } from 'mime';
|
||||
import { join } from 'path';
|
||||
import { Duplex } from 'stream';
|
||||
import { Server as WebSocketServer, WebSocket } from 'ws';
|
||||
import { gzip } from 'zlib';
|
||||
|
||||
interface Subscriber {
|
||||
resolve: (msg: IncomingMessage) => void;
|
||||
reject: (err?: Error) => void;
|
||||
promise: Promise<IncomingMessage>;
|
||||
}
|
||||
|
||||
type TestIncomingMessage = IncomingMessage & { postBody?: Promise<string> };
|
||||
|
||||
export class TestServer {
|
||||
PORT?: number;
|
||||
PREFIX?: string;
|
||||
CROSS_PROCESS_PREFIX?: string;
|
||||
EMPTY_PAGE?: string;
|
||||
|
||||
#dirPath: string;
|
||||
#server: HttpsServer | HttpServer;
|
||||
#wsServer: WebSocketServer;
|
||||
|
||||
#startTime = new Date();
|
||||
#cachedPathPrefix?: string;
|
||||
|
||||
#connections = new Set<Duplex>();
|
||||
#routes = new Map<
|
||||
string,
|
||||
(msg: IncomingMessage, res: ServerResponse) => void
|
||||
>();
|
||||
#auths = new Map<string, { username: string; password: string }>();
|
||||
#csp = new Map<string, string>();
|
||||
#gzipRoutes = new Set<string>();
|
||||
#requestSubscribers = new Map<string, Subscriber>();
|
||||
|
||||
static async create(dirPath: string, port: number): Promise<TestServer> {
|
||||
const server = new TestServer(dirPath, port);
|
||||
await new Promise((x) => server.#server.once('listening', x));
|
||||
return server;
|
||||
}
|
||||
|
||||
static async createHTTPS(dirPath: string, port: number): Promise<TestServer> {
|
||||
const server = new TestServer(dirPath, port, {
|
||||
key: readFileSync(join(__dirname, '..', 'key.pem')),
|
||||
cert: readFileSync(join(__dirname, '..', 'cert.pem')),
|
||||
passphrase: 'aaaa',
|
||||
});
|
||||
await new Promise((x) => server.#server.once('listening', x));
|
||||
return server;
|
||||
}
|
||||
|
||||
constructor(dirPath: string, port: number, sslOptions?: HttpsServerOptions) {
|
||||
this.#dirPath = dirPath;
|
||||
|
||||
if (sslOptions) {
|
||||
this.#server = createHttpsServer(sslOptions, this.#onRequest);
|
||||
} else {
|
||||
this.#server = createHttpServer(this.#onRequest);
|
||||
}
|
||||
this.#server.on('connection', this.#onServerConnection);
|
||||
this.#wsServer = new WebSocketServer({ server: this.#server });
|
||||
this.#wsServer.on('connection', this.#onWebSocketConnection);
|
||||
this.#server.listen(port);
|
||||
}
|
||||
|
||||
#onServerConnection = (connection: Duplex): void => {
|
||||
this.#connections.add(connection);
|
||||
// ECONNRESET is a legit error given
|
||||
// that tab closing simply kills process.
|
||||
connection.on('error', (error) => {
|
||||
if ((error as NodeJS.ErrnoException).code !== 'ECONNRESET') {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
connection.once('close', () => this.#connections.delete(connection));
|
||||
};
|
||||
|
||||
enableHTTPCache(pathPrefix: string): void {
|
||||
this.#cachedPathPrefix = pathPrefix;
|
||||
}
|
||||
|
||||
setAuth(path: string, username: string, password: string): void {
|
||||
this.#auths.set(path, { username, password });
|
||||
}
|
||||
|
||||
enableGzip(path: string): void {
|
||||
this.#gzipRoutes.add(path);
|
||||
}
|
||||
|
||||
setCSP(path: string, csp: string): void {
|
||||
this.#csp.set(path, csp);
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
this.reset();
|
||||
for (const socket of this.#connections) {
|
||||
socket.destroy();
|
||||
}
|
||||
this.#connections.clear();
|
||||
await new Promise((x) => this.#server.close(x));
|
||||
}
|
||||
|
||||
setRoute(
|
||||
path: string,
|
||||
handler: (req: IncomingMessage, res: ServerResponse) => void
|
||||
): void {
|
||||
this.#routes.set(path, handler);
|
||||
}
|
||||
|
||||
setRedirect(from: string, to: string): void {
|
||||
this.setRoute(from, (_, res) => {
|
||||
res.writeHead(302, { location: to });
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
|
||||
waitForRequest(path: string): Promise<TestIncomingMessage> {
|
||||
const subscriber = this.#requestSubscribers.get(path);
|
||||
if (subscriber) {
|
||||
return subscriber.promise;
|
||||
}
|
||||
let resolve!: (value: IncomingMessage) => void;
|
||||
let reject!: (reason?: Error) => void;
|
||||
const promise = new Promise<IncomingMessage>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
this.#requestSubscribers.set(path, { resolve, reject, promise });
|
||||
return promise;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.#routes.clear();
|
||||
this.#auths.clear();
|
||||
this.#csp.clear();
|
||||
this.#gzipRoutes.clear();
|
||||
const error = new Error('Static Server has been reset');
|
||||
for (const subscriber of this.#requestSubscribers.values()) {
|
||||
subscriber.reject.call(undefined, error);
|
||||
}
|
||||
this.#requestSubscribers.clear();
|
||||
}
|
||||
|
||||
#onRequest: RequestListener = (
|
||||
request: TestIncomingMessage,
|
||||
response
|
||||
): void => {
|
||||
request.on('error', (error: { code: string }) => {
|
||||
if (error.code === 'ECONNRESET') {
|
||||
response.end();
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
request.postBody = new Promise((resolve) => {
|
||||
let body = '';
|
||||
request.on('data', (chunk: string) => (body += chunk));
|
||||
request.on('end', () => resolve(body));
|
||||
});
|
||||
assert(request.url);
|
||||
const url = new URL(request.url, `https://${request.headers.host}`);
|
||||
const path = url.pathname + url.search;
|
||||
const auth = this.#auths.get(path);
|
||||
if (auth) {
|
||||
const credentials = Buffer.from(
|
||||
(request.headers.authorization || '').split(' ')[1] || '',
|
||||
'base64'
|
||||
).toString();
|
||||
if (credentials !== `${auth.username}:${auth.password}`) {
|
||||
response.writeHead(401, {
|
||||
'WWW-Authenticate': 'Basic realm="Secure Area"',
|
||||
});
|
||||
response.end('HTTP Error 401 Unauthorized: Access is denied');
|
||||
return;
|
||||
}
|
||||
}
|
||||
const subscriber = this.#requestSubscribers.get(path);
|
||||
if (subscriber) {
|
||||
subscriber.resolve.call(undefined, request);
|
||||
this.#requestSubscribers.delete(path);
|
||||
}
|
||||
const handler = this.#routes.get(path);
|
||||
if (handler) {
|
||||
handler.call(undefined, request, response);
|
||||
} else {
|
||||
this.serveFile(request, response, path);
|
||||
}
|
||||
};
|
||||
|
||||
serveFile(
|
||||
request: IncomingMessage,
|
||||
response: ServerResponse,
|
||||
pathName: string
|
||||
): void {
|
||||
if (pathName === '/') {
|
||||
pathName = '/index.html';
|
||||
}
|
||||
const filePath = join(this.#dirPath, pathName.substring(1));
|
||||
|
||||
if (this.#cachedPathPrefix && filePath.startsWith(this.#cachedPathPrefix)) {
|
||||
if (request.headers['if-modified-since']) {
|
||||
response.statusCode = 304; // not modified
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
response.setHeader('Cache-Control', 'public, max-age=31536000');
|
||||
response.setHeader('Last-Modified', this.#startTime.toISOString());
|
||||
} else {
|
||||
response.setHeader('Cache-Control', 'no-cache, no-store');
|
||||
}
|
||||
const csp = this.#csp.get(pathName);
|
||||
if (csp) {
|
||||
response.setHeader('Content-Security-Policy', csp);
|
||||
}
|
||||
|
||||
readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
response.statusCode = 404;
|
||||
response.end(`File not found: ${filePath}`);
|
||||
return;
|
||||
}
|
||||
const mimeType = getMimeType(filePath);
|
||||
if (mimeType) {
|
||||
const isTextEncoding = /^text\/|^application\/(javascript|json)/.test(
|
||||
mimeType
|
||||
);
|
||||
const contentType = isTextEncoding
|
||||
? `${mimeType}; charset=utf-8`
|
||||
: mimeType;
|
||||
response.setHeader('Content-Type', contentType);
|
||||
}
|
||||
if (this.#gzipRoutes.has(pathName)) {
|
||||
response.setHeader('Content-Encoding', 'gzip');
|
||||
gzip(data, (_, result) => {
|
||||
response.end(result);
|
||||
});
|
||||
} else {
|
||||
response.end(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#onWebSocketConnection = (socket: WebSocket): void => {
|
||||
socket.send('opened');
|
||||
};
|
||||
}
|
11
utils/testserver/tsconfig.json
Normal file
11
utils/testserver/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib",
|
||||
"composite": true,
|
||||
"allowJs": true,
|
||||
"module": "CommonJS"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user