chore: use composite
builds for tests (#8522)
This commit is contained in:
parent
cba58a12c4
commit
80373f7a12
@ -1,16 +1,8 @@
|
|||||||
test/assets/modernizr.js
|
assets/
|
||||||
third_party/*
|
build/
|
||||||
utils/doclint/check_public_api/test/
|
coverage/
|
||||||
node6/*
|
doclint/
|
||||||
node6-test/*
|
|
||||||
experimental/
|
|
||||||
lib/
|
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/
|
test-ts-types/
|
||||||
website/
|
tsconfig.tsbuildinfo
|
||||||
docs-dist/
|
vendor/
|
38
.gitignore
vendored
38
.gitignore
vendored
@ -1,26 +1,20 @@
|
|||||||
/node_modules/
|
.DS_Store
|
||||||
test-ts-types/**/node_modules
|
.vscode
|
||||||
test-ts-types/**/dist/
|
/.dev_profile*
|
||||||
/test/output-chromium
|
|
||||||
/test/output-firefox
|
|
||||||
/test/test-user-data-dir*
|
|
||||||
/.local-chromium/
|
/.local-chromium/
|
||||||
/.local-firefox/
|
/.local-firefox/
|
||||||
/.dev_profile*
|
/test/test-user-data-dir*
|
||||||
.DS_Store
|
build/
|
||||||
*.swp
|
|
||||||
*.pyc
|
|
||||||
.vscode
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
/node6
|
|
||||||
lib
|
|
||||||
test/coverage.json
|
|
||||||
temp/
|
|
||||||
new-docs/
|
|
||||||
puppeteer*.tgz
|
|
||||||
docs-api-json/
|
|
||||||
docs-dist/
|
|
||||||
coverage
|
coverage
|
||||||
website/docs
|
coverage/
|
||||||
|
doclint/
|
||||||
|
docs-api-json/
|
||||||
docs/api.html
|
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.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const base = require('./base.js');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...base,
|
reporter: 'dot',
|
||||||
require: [
|
logLevel: 'debug',
|
||||||
'./test/mocha-ts-require',
|
require: ['./test/build/mocha-utils.js', 'source-map-support/register'],
|
||||||
'./test/mocha-utils.ts',
|
spec: 'test/build/*.spec.js',
|
||||||
'source-map-support/register',
|
exit: !!process.env.CI,
|
||||||
],
|
|
||||||
spec: 'test/*.spec.ts',
|
|
||||||
extension: ['js', 'ts'],
|
|
||||||
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 * 1000,
|
@ -1,17 +1,17 @@
|
|||||||
node_modules/
|
.local-chromium/
|
||||||
lib/
|
.local-firefox/
|
||||||
third_party/
|
assets/
|
||||||
vendor/
|
build/
|
||||||
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
package.json
|
|
||||||
docs-api-json/
|
|
||||||
docs-dist/
|
|
||||||
website/
|
|
||||||
experimental/
|
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
|
coverage/
|
||||||
|
doclint/
|
||||||
|
docs-api-json/
|
||||||
|
lib/
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
package.json
|
||||||
|
test-ts-types/
|
||||||
test/assets/
|
test/assets/
|
||||||
/.local-chromium/
|
tsconfig.tsbuildinfo
|
||||||
/.local-firefox/
|
vendor/
|
||||||
test-ts-types
|
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"
|
"node": ">=14.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run build && npm run lint --silent && npm run test:unit:coverage",
|
"test": "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:unit": "npm run build:test && mocha",
|
||||||
"test:unit:firefox": "cross-env PUPPETEER_PRODUCT=firefox npm run test:unit",
|
"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:coverage": "c8 --check-coverage --lines 94 npm run test:unit",
|
||||||
"test:unit:chrome-headless": "cross-env HEADLESS=chrome 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:protocol-revision": "ts-node -s scripts/ensure-correct-devtools-protocol-package",
|
||||||
"test:pinned-deps": "ts-node -s scripts/ensure-pinned-deps",
|
"test:pinned-deps": "ts-node -s scripts/ensure-pinned-deps",
|
||||||
"test:install": "scripts/test-install.sh",
|
"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",
|
"test:types": "ts-node -s scripts/test-ts-definition-files.ts",
|
||||||
"prepublishOnly": "npm run build",
|
"prepublishOnly": "npm run build",
|
||||||
"prepare": "node typescript-if-required.js && ([[ $HUSKY = 0 ]] || husky install)",
|
"prepare": "node typescript-if-required.js && ([[ $HUSKY = 0 ]] || husky install)",
|
||||||
@ -54,11 +54,10 @@
|
|||||||
"clean:lib": "rimraf lib",
|
"clean:lib": "rimraf lib",
|
||||||
"clean:docs": "rimraf docs-api-json",
|
"clean:docs": "rimraf docs-api-json",
|
||||||
"build": "npm run build:tsc && npm run generate:types && npm run generate:esm-package-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": "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": "tsc -b src/tsconfig.cjs.json",
|
|
||||||
"build:tsc:cjs:compat": "tsc -b compat/cjs/tsconfig.json"
|
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib",
|
"lib",
|
||||||
@ -104,6 +103,7 @@
|
|||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.16.0",
|
"eslint": "8.16.0",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
|
"eslint-formatter-codeframe": "7.32.1",
|
||||||
"eslint-plugin-import": "2.26.0",
|
"eslint-plugin-import": "2.26.0",
|
||||||
"eslint-plugin-mocha": "10.0.5",
|
"eslint-plugin-mocha": "10.0.5",
|
||||||
"eslint-plugin-prettier": "4.0.0",
|
"eslint-plugin-prettier": "4.0.0",
|
||||||
@ -122,7 +122,6 @@
|
|||||||
"sinon": "14.0.0",
|
"sinon": "14.0.0",
|
||||||
"source-map-support": "0.5.21",
|
"source-map-support": "0.5.21",
|
||||||
"text-diff": "1.0.1",
|
"text-diff": "1.0.1",
|
||||||
"ts-node": "10.8.0",
|
|
||||||
"typescript": "4.7.2"
|
"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",
|
"outDir": "../lib/cjs/puppeteer",
|
||||||
"module": "CommonJS"
|
"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",
|
"outDir": "../lib/esm/puppeteer",
|
||||||
"module": "esnext"
|
"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.
|
* 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 sinon from 'sinon';
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
|
|
@ -20,10 +20,10 @@ import expect from 'expect';
|
|||||||
import {
|
import {
|
||||||
NetworkManager,
|
NetworkManager,
|
||||||
NetworkManagerEmittedEvents,
|
NetworkManagerEmittedEvents,
|
||||||
} from '../lib/cjs/puppeteer/common/NetworkManager.js';
|
} from '../../lib/cjs/puppeteer/common/NetworkManager.js';
|
||||||
import { HTTPRequest } from '../lib/cjs/puppeteer/common/HTTPRequest.js';
|
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/FrameManager.js';
|
import { Frame } from '../../lib/cjs/puppeteer/common/FrameManager.js';
|
||||||
|
|
||||||
class MockCDPSession extends EventEmitter {
|
class MockCDPSession extends EventEmitter {
|
||||||
async send(): Promise<any> {}
|
async send(): Promise<any> {}
|
@ -22,7 +22,7 @@ import {
|
|||||||
describeChromeOnly,
|
describeChromeOnly,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} 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';
|
import utils from './utils.js';
|
||||||
|
|
||||||
describeChromeOnly('AriaQueryHandler', () => {
|
describeChromeOnly('AriaQueryHandler', () => {
|
@ -25,7 +25,7 @@ import {
|
|||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||||
|
|
||||||
import utils from './utils.js';
|
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 () {
|
describe('ElementHandle specs', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
@ -32,7 +32,7 @@ describe('Fixtures', function () {
|
|||||||
dumpio: true,
|
dumpio: true,
|
||||||
});
|
});
|
||||||
const res = spawn('node', [
|
const res = spawn('node', [
|
||||||
path.join(__dirname, 'fixtures', 'dumpio.js'),
|
path.join(__dirname, '../fixtures', 'dumpio.js'),
|
||||||
puppeteerPath,
|
puppeteerPath,
|
||||||
JSON.stringify(options),
|
JSON.stringify(options),
|
||||||
]);
|
]);
|
||||||
@ -47,7 +47,7 @@ describe('Fixtures', function () {
|
|||||||
const { spawn } = await import('child_process');
|
const { spawn } = await import('child_process');
|
||||||
const options = Object.assign({}, defaultBrowserOptions, { dumpio: true });
|
const options = Object.assign({}, defaultBrowserOptions, { dumpio: true });
|
||||||
const res = spawn('node', [
|
const res = spawn('node', [
|
||||||
path.join(__dirname, 'fixtures', 'dumpio.js'),
|
path.join(__dirname, '../fixtures', 'dumpio.js'),
|
||||||
puppeteerPath,
|
puppeteerPath,
|
||||||
JSON.stringify(options),
|
JSON.stringify(options),
|
||||||
]);
|
]);
|
||||||
@ -64,7 +64,7 @@ describe('Fixtures', function () {
|
|||||||
dumpio: false,
|
dumpio: false,
|
||||||
});
|
});
|
||||||
const res = spawn('node', [
|
const res = spawn('node', [
|
||||||
path.join(__dirname, 'fixtures', 'closeme.js'),
|
path.join(__dirname, '../fixtures', 'closeme.js'),
|
||||||
puppeteerPath,
|
puppeteerPath,
|
||||||
JSON.stringify(options),
|
JSON.stringify(options),
|
||||||
]);
|
]);
|
@ -22,7 +22,7 @@ import {
|
|||||||
setupTestPageAndContextHooks,
|
setupTestPageAndContextHooks,
|
||||||
itFailsFirefox,
|
itFailsFirefox,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} 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 () {
|
describe('Frame specs', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
@ -31,7 +31,7 @@ const mkdtempAsync = promisify(fs.mkdtemp);
|
|||||||
|
|
||||||
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
|
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 () {
|
describeChromeOnly('headful tests', function () {
|
||||||
/* These tests fire up an actual browser so let's
|
/* These tests fire up an actual browser so let's
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
|
import { TLSSocket } from 'tls';
|
||||||
import {
|
import {
|
||||||
getTestState,
|
getTestState,
|
||||||
describeFailsFirefox,
|
describeFailsFirefox,
|
||||||
@ -65,7 +66,9 @@ describe('ignoreHTTPSErrors', function () {
|
|||||||
]);
|
]);
|
||||||
const securityDetails = response.securityDetails();
|
const securityDetails = response.securityDetails();
|
||||||
expect(securityDetails.issuer()).toBe('puppeteer-tests');
|
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.protocol()).toBe(protocol);
|
||||||
expect(securityDetails.subjectName()).toBe('puppeteer-tests');
|
expect(securityDetails.subjectName()).toBe('puppeteer-tests');
|
||||||
expect(securityDetails.validFrom()).toBe(1589357069);
|
expect(securityDetails.validFrom()).toBe(1589357069);
|
||||||
@ -94,7 +97,9 @@ describe('ignoreHTTPSErrors', function () {
|
|||||||
expect(responses.length).toBe(2);
|
expect(responses.length).toBe(2);
|
||||||
expect(responses[0].status()).toBe(302);
|
expect(responses[0].status()).toBe(302);
|
||||||
const securityDetails = responses[0].securityDetails();
|
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);
|
expect(securityDetails.protocol()).toBe(protocol);
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -23,7 +23,7 @@ import {
|
|||||||
describeFailsFirefox,
|
describeFailsFirefox,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} 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 () {
|
describe('input tests', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
||||||
@ -228,9 +228,9 @@ describe('input tests', function () {
|
|||||||
.accept([
|
.accept([
|
||||||
path.relative(
|
path.relative(
|
||||||
process.cwd(),
|
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_));
|
.catch((error_) => (error = error_));
|
||||||
expect(error).not.toBe(null);
|
expect(error).not.toBe(null);
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import { JSHandle } from '../lib/cjs/puppeteer/common/JSHandle.js';
|
import { JSHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
||||||
import {
|
import {
|
||||||
getTestState,
|
getTestState,
|
||||||
setupTestBrowserHooks,
|
setupTestBrowserHooks,
|
@ -23,7 +23,7 @@ import {
|
|||||||
setupTestPageAndContextHooks,
|
setupTestPageAndContextHooks,
|
||||||
itFailsFirefox,
|
itFailsFirefox,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} 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 () {
|
describe('Keyboard', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
@ -29,7 +29,8 @@ import {
|
|||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import rimraf from 'rimraf';
|
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 mkdtempAsync = promisify(fs.mkdtemp);
|
||||||
const readFileAsync = promisify(fs.readFile);
|
const readFileAsync = promisify(fs.readFile);
|
||||||
@ -677,7 +678,9 @@ describe('Launcher specs', function () {
|
|||||||
expect(error).toBe(null);
|
expect(error).toBe(null);
|
||||||
expect(response.ok()).toBe(true);
|
expect(response.ok()).toBe(true);
|
||||||
expect(response.securityDetails()).toBeTruthy();
|
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);
|
expect(response.securityDetails().protocol()).toBe(protocol);
|
||||||
await page.close();
|
await page.close();
|
||||||
await browser.close();
|
await browser.close();
|
@ -24,17 +24,17 @@ import sinon from 'sinon';
|
|||||||
import {
|
import {
|
||||||
Browser,
|
Browser,
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
} from '../lib/cjs/puppeteer/common/Browser.js';
|
} from '../../lib/cjs/puppeteer/common/Browser.js';
|
||||||
import { isErrorLike } from '../lib/cjs/puppeteer/common/util.js';
|
import { isErrorLike } from '../../lib/cjs/puppeteer/common/util.js';
|
||||||
import { Page } from '../lib/cjs/puppeteer/common/Page.js';
|
import { Page } from '../../lib/cjs/puppeteer/common/Page.js';
|
||||||
import { PuppeteerNode } from '../lib/cjs/puppeteer/node/Puppeteer.js';
|
import { PuppeteerNode } from '../../lib/cjs/puppeteer/node/Puppeteer.js';
|
||||||
import puppeteer from '../lib/cjs/puppeteer/puppeteer.js';
|
import puppeteer from '../../lib/cjs/puppeteer/puppeteer.js';
|
||||||
import { TestServer } from '../utils/testserver/index.js';
|
|
||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
|
import { TestServer } from '../../utils/testserver/lib/index.js';
|
||||||
|
|
||||||
const setupServer = async () => {
|
const setupServer = async () => {
|
||||||
const assetsPath = path.join(__dirname, 'assets');
|
const assetsPath = path.join(__dirname, '../assets');
|
||||||
const cachedPath = path.join(__dirname, 'assets', 'cached');
|
const cachedPath = path.join(__dirname, '../assets', 'cached');
|
||||||
|
|
||||||
const port = 8907;
|
const port = 8907;
|
||||||
const server = await TestServer.create(assetsPath, port);
|
const server = await TestServer.create(assetsPath, port);
|
||||||
@ -122,8 +122,8 @@ declare module 'expect/build/types' {
|
|||||||
|
|
||||||
const setupGoldenAssertions = (): void => {
|
const setupGoldenAssertions = (): void => {
|
||||||
const suffix = product.toLowerCase();
|
const suffix = product.toLowerCase();
|
||||||
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)) {
|
||||||
rimraf.sync(OUTPUT_DIR);
|
rimraf.sync(OUTPUT_DIR);
|
||||||
}
|
}
|
||||||
@ -140,8 +140,8 @@ interface PuppeteerTestState {
|
|||||||
defaultBrowserOptions: {
|
defaultBrowserOptions: {
|
||||||
[x: string]: any;
|
[x: string]: any;
|
||||||
};
|
};
|
||||||
server: any;
|
server: TestServer;
|
||||||
httpsServer: any;
|
httpsServer: TestServer;
|
||||||
isFirefox: boolean;
|
isFirefox: boolean;
|
||||||
isChrome: boolean;
|
isChrome: boolean;
|
||||||
isHeadless: boolean;
|
isHeadless: boolean;
|
||||||
@ -299,7 +299,7 @@ export const mochaHooks = {
|
|||||||
state.isChrome = isChrome;
|
state.isChrome = isChrome;
|
||||||
state.isHeadless = isHeadless;
|
state.isHeadless = isHeadless;
|
||||||
state.headless = headless;
|
state.headless = headless;
|
||||||
state.puppeteerPath = path.resolve(path.join(__dirname, '..'));
|
state.puppeteerPath = path.resolve(path.join(__dirname, '../..'));
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -21,7 +21,7 @@ import {
|
|||||||
setupTestPageAndContextHooks,
|
setupTestPageAndContextHooks,
|
||||||
itFailsFirefox,
|
itFailsFirefox,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} 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 {
|
interface Dimensions {
|
||||||
x: number;
|
x: number;
|
@ -27,7 +27,7 @@ import {
|
|||||||
itChromeOnly,
|
itChromeOnly,
|
||||||
itFirefoxOnly,
|
itFirefoxOnly,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} 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 () {
|
describe('network', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
||||||
@ -363,7 +363,7 @@ describe('network', function () {
|
|||||||
|
|
||||||
const response = await page.goto(server.PREFIX + '/pptr.png');
|
const response = await page.goto(server.PREFIX + '/pptr.png');
|
||||||
const imageBuffer = fs.readFileSync(
|
const imageBuffer = fs.readFileSync(
|
||||||
path.join(__dirname, 'assets', 'pptr.png')
|
path.join(__dirname, '../assets', 'pptr.png')
|
||||||
);
|
);
|
||||||
const responseBuffer = await response.buffer();
|
const responseBuffer = await response.buffer();
|
||||||
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
||||||
@ -374,7 +374,7 @@ describe('network', function () {
|
|||||||
server.enableGzip('/pptr.png');
|
server.enableGzip('/pptr.png');
|
||||||
const response = await page.goto(server.PREFIX + '/pptr.png');
|
const response = await page.goto(server.PREFIX + '/pptr.png');
|
||||||
const imageBuffer = fs.readFileSync(
|
const imageBuffer = fs.readFileSync(
|
||||||
path.join(__dirname, 'assets', 'pptr.png')
|
path.join(__dirname, '../assets', 'pptr.png')
|
||||||
);
|
);
|
||||||
const responseBuffer = await response.buffer();
|
const responseBuffer = await response.buffer();
|
||||||
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
@ -25,7 +25,7 @@ import {
|
|||||||
Browser,
|
Browser,
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
Page,
|
Page,
|
||||||
} from '../lib/cjs/puppeteer/api-docs-entry.js';
|
} from '../../lib/cjs/puppeteer/api-docs-entry.js';
|
||||||
|
|
||||||
describeChromeOnly('OOPIF', function () {
|
describeChromeOnly('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 */
|
@ -26,9 +26,9 @@ import {
|
|||||||
itFailsFirefox,
|
itFailsFirefox,
|
||||||
describeFailsFirefox,
|
describeFailsFirefox,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||||
import { Page, Metrics } from '../lib/cjs/puppeteer/common/Page.js';
|
import { Page, Metrics } from '../../lib/cjs/puppeteer/common/Page.js';
|
||||||
import { CDPSession } from '../lib/cjs/puppeteer/common/Connection.js';
|
import { CDPSession } from '../../lib/cjs/puppeteer/common/Connection.js';
|
||||||
import { JSHandle } from '../lib/cjs/puppeteer/common/JSHandle.js';
|
import { JSHandle } from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
||||||
|
|
||||||
describe('Page', function () {
|
describe('Page', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
||||||
@ -967,7 +967,7 @@ describe('Page', function () {
|
|||||||
it('should throw exception in page context', async () => {
|
it('should throw exception in page context', async () => {
|
||||||
const { page } = getTestState();
|
const { page } = getTestState();
|
||||||
|
|
||||||
await page.exposeFunction('woof', function () {
|
await page.exposeFunction('woof', () => {
|
||||||
throw new Error('WOOF WOOF');
|
throw new Error('WOOF WOOF');
|
||||||
});
|
});
|
||||||
const { message, stack } = await page.evaluate(async () => {
|
const { message, stack } = await page.evaluate(async () => {
|
||||||
@ -978,7 +978,7 @@ describe('Page', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
expect(message).toBe('WOOF WOOF');
|
expect(message).toBe('WOOF WOOF');
|
||||||
expect(stack).toContain(__filename);
|
expect(stack).toContain('page.spec.ts');
|
||||||
});
|
});
|
||||||
it('should support throwing "null"', async () => {
|
it('should support throwing "null"', async () => {
|
||||||
const { page } = getTestState();
|
const { page } = getTestState();
|
||||||
@ -1425,7 +1425,7 @@ describe('Page', function () {
|
|||||||
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.addScriptTag({
|
await page.addScriptTag({
|
||||||
path: path.join(__dirname, 'assets/es6/es6pathimport.js'),
|
path: path.join(__dirname, '../assets/es6/es6pathimport.js'),
|
||||||
type: 'module',
|
type: 'module',
|
||||||
});
|
});
|
||||||
await page.waitForFunction('window.__es6injected');
|
await page.waitForFunction('window.__es6injected');
|
||||||
@ -1462,7 +1462,7 @@ describe('Page', function () {
|
|||||||
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const scriptHandle = await page.addScriptTag({
|
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(scriptHandle.asElement()).not.toBeNull();
|
||||||
expect(await page.evaluate(() => globalThis.__injected)).toBe(42);
|
expect(await page.evaluate(() => globalThis.__injected)).toBe(42);
|
||||||
@ -1473,7 +1473,7 @@ describe('Page', function () {
|
|||||||
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.addScriptTag({
|
await page.addScriptTag({
|
||||||
path: path.join(__dirname, 'assets/injectedfile.js'),
|
path: path.join(__dirname, '../assets/injectedfile.js'),
|
||||||
});
|
});
|
||||||
const result = await page.evaluate(
|
const result = await page.evaluate(
|
||||||
() => globalThis.__injectedError.stack
|
() => globalThis.__injectedError.stack
|
||||||
@ -1572,7 +1572,7 @@ describe('Page', function () {
|
|||||||
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const styleHandle = await page.addStyleTag({
|
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(styleHandle.asElement()).not.toBeNull();
|
||||||
expect(
|
expect(
|
||||||
@ -1587,7 +1587,7 @@ describe('Page', function () {
|
|||||||
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.addStyleTag({
|
await page.addStyleTag({
|
||||||
path: path.join(__dirname, 'assets/injectedstyle.css'),
|
path: path.join(__dirname, '../assets/injectedstyle.css'),
|
||||||
});
|
});
|
||||||
const styleHandle = await page.$('style');
|
const styleHandle = await page.$('style');
|
||||||
const styleContent = await page.evaluate(
|
const styleContent = await page.evaluate(
|
||||||
@ -1717,7 +1717,7 @@ describe('Page', function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputFile = __dirname + '/assets/output.pdf';
|
const outputFile = __dirname + '/../assets/output.pdf';
|
||||||
await page.pdf({ path: outputFile });
|
await page.pdf({ path: outputFile });
|
||||||
expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0);
|
expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0);
|
||||||
fs.unlinkSync(outputFile);
|
fs.unlinkSync(outputFile);
|
@ -23,8 +23,9 @@ import {
|
|||||||
itFailsWindows,
|
itFailsWindows,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||||
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';
|
||||||
|
|
||||||
const HOSTNAME = os.hostname().toLowerCase();
|
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
|
* Requests to localhost do not get proxied by default. Create a URL using the hostname
|
||||||
* instead.
|
* instead.
|
||||||
*/
|
*/
|
||||||
function getEmptyPageUrl(server: { PORT: number; EMPTY_PAGE: string }): string {
|
function getEmptyPageUrl(server: TestServer): string {
|
||||||
const emptyPagePath = new URL(server.EMPTY_PAGE).pathname;
|
const emptyPagePath = new URL(server.EMPTY_PAGE).pathname;
|
||||||
|
|
||||||
return `http://${HOSTNAME}:${server.PORT}${emptyPagePath}`;
|
return `http://${HOSTNAME}:${server.PORT}${emptyPagePath}`;
|
@ -19,7 +19,7 @@ import {
|
|||||||
setupTestBrowserHooks,
|
setupTestBrowserHooks,
|
||||||
setupTestPageAndContextHooks,
|
setupTestPageAndContextHooks,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} 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 () {
|
describe('querySelector', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
@ -24,8 +24,8 @@ import {
|
|||||||
setupTestPageAndContextHooks,
|
setupTestPageAndContextHooks,
|
||||||
describeFailsFirefox,
|
describeFailsFirefox,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||||
import { ActionResult } from '../lib/cjs/puppeteer/api-docs-entry.js';
|
import { ActionResult } from '../../lib/cjs/puppeteer/api-docs-entry.js';
|
||||||
import { InterceptResolutionAction } from '../lib/cjs/puppeteer/common/HTTPRequest.js';
|
import { InterceptResolutionAction } from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
|
||||||
|
|
||||||
describe('request interception', function () {
|
describe('request interception', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
||||||
@ -592,7 +592,7 @@ describe('request interception', function () {
|
|||||||
request.continue({}, 0);
|
request.continue({}, 0);
|
||||||
});
|
});
|
||||||
await page.goto(
|
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.size).toBe(2);
|
||||||
expect(urls.has('one-style.html')).toBe(true);
|
expect(urls.has('one-style.html')).toBe(true);
|
||||||
@ -827,7 +827,7 @@ describe('request interception', function () {
|
|||||||
await page.setRequestInterception(true);
|
await page.setRequestInterception(true);
|
||||||
page.on('request', (request) => {
|
page.on('request', (request) => {
|
||||||
const imageBuffer = fs.readFileSync(
|
const imageBuffer = fs.readFileSync(
|
||||||
path.join(__dirname, 'assets', 'pptr.png')
|
path.join(__dirname, '../assets', 'pptr.png')
|
||||||
);
|
);
|
||||||
request.respond(
|
request.respond(
|
||||||
{
|
{
|
@ -507,7 +507,7 @@ describe('request interception', function () {
|
|||||||
request.continue();
|
request.continue();
|
||||||
});
|
});
|
||||||
await page.goto(
|
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.size).toBe(2);
|
||||||
expect(urls.has('one-style.html')).toBe(true);
|
expect(urls.has('one-style.html')).toBe(true);
|
||||||
@ -759,7 +759,7 @@ describe('request interception', function () {
|
|||||||
await page.setRequestInterception(true);
|
await page.setRequestInterception(true);
|
||||||
page.on('request', (request) => {
|
page.on('request', (request) => {
|
||||||
const imageBuffer = fs.readFileSync(
|
const imageBuffer = fs.readFileSync(
|
||||||
path.join(__dirname, 'assets', 'pptr.png')
|
path.join(__dirname, '../assets', 'pptr.png')
|
||||||
);
|
);
|
||||||
request.respond({
|
request.respond({
|
||||||
contentType: 'image/png',
|
contentType: 'image/png',
|
@ -23,7 +23,7 @@ import {
|
|||||||
setupTestPageAndContextHooks,
|
setupTestPageAndContextHooks,
|
||||||
itFailsFirefox,
|
itFailsFirefox,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} 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 () {
|
describe('Target', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
@ -32,7 +32,7 @@ describeChromeOnly('Tracing', function () {
|
|||||||
const { defaultBrowserOptions, puppeteer } = getTestState();
|
const { defaultBrowserOptions, puppeteer } = getTestState();
|
||||||
browser = await puppeteer.launch(defaultBrowserOptions);
|
browser = await puppeteer.launch(defaultBrowserOptions);
|
||||||
page = await browser.newPage();
|
page = await browser.newPage();
|
||||||
outputFile = path.join(__dirname, 'assets', 'trace.json');
|
outputFile = path.join(__dirname, 'trace.json');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
@ -17,13 +17,10 @@
|
|||||||
// TODO (@jackfranklin): convert to TS and enable type checking.
|
// TODO (@jackfranklin): convert to TS and enable type checking.
|
||||||
|
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const expect = require('expect');
|
const expect = require('expect');
|
||||||
const GoldenUtils = require('./golden-utils.js');
|
const GoldenUtils = require('./golden-utils.js');
|
||||||
const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json'))
|
const PROJECT_ROOT = path.join(__dirname, '..', '..');
|
||||||
? path.join(__dirname, '..')
|
|
||||||
: path.join(__dirname, '..', '..');
|
|
||||||
|
|
||||||
const utils = (module.exports = {
|
const utils = (module.exports = {
|
||||||
extendExpectWithToBeGolden: function (goldenDir, outputDir) {
|
extendExpectWithToBeGolden: function (goldenDir, outputDir) {
|
@ -22,8 +22,8 @@ import {
|
|||||||
describeFailsFirefox,
|
describeFailsFirefox,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
import { WebWorker } from '../lib/cjs/puppeteer/common/WebWorker.js';
|
import { WebWorker } from '../../lib/cjs/puppeteer/common/WebWorker.js';
|
||||||
import { ConsoleMessage } from '../lib/cjs/puppeteer/common/ConsoleMessage.js';
|
import { ConsoleMessage } from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
|
||||||
const { waitEvent } = utils;
|
const { waitEvent } = utils;
|
||||||
|
|
||||||
describeFailsFirefox('Workers', function () {
|
describeFailsFirefox('Workers', function () {
|
@ -1,6 +1,21 @@
|
|||||||
{
|
{
|
||||||
"extends": "./tsconfig.test.json",
|
|
||||||
"compilerOptions": {
|
"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 mdBuilder = require('./MDBuilder.js');
|
||||||
const Documentation = require('./Documentation.js');
|
const Documentation = require('./Documentation.js');
|
||||||
const Message = require('../Message.js');
|
const Message = require('../Message.js');
|
||||||
const {
|
|
||||||
MODULES_TO_CHECK_FOR_COVERAGE,
|
const MODULES_TO_CHECK_FOR_COVERAGE = {
|
||||||
} = require('../../../test/coverage-utils.js');
|
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([
|
const EXCLUDE_PROPERTIES = new Set([
|
||||||
'Browser.create',
|
'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",
|
"name": "@pptr/testserver",
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"description": "testing server",
|
"description": "testing server",
|
||||||
"main": "index.js",
|
"main": "lib/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"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