chore(firefox): lint against the juggler protocol (#94)
This commit is contained in:
parent
c370327b4d
commit
492f539310
|
@ -8,6 +8,7 @@ node6-test/*
|
|||
node6-testrunner/*
|
||||
lib/
|
||||
*.js
|
||||
src/chromium/protocol.d.ts
|
||||
src/generated/*
|
||||
src/chromium/protocol.d.ts
|
||||
src/firefox/protocol.d.ts
|
||||
src/webkit/protocol.d.ts
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
package-lock.json
|
||||
yarn.lock
|
||||
/node6
|
||||
/src/chromium/protocol.d.ts
|
||||
/src/generated/*
|
||||
/src/chromium/protocol.d.ts
|
||||
/src/firefox/protocol.d.ts
|
||||
/src/webkit/protocol.d.ts
|
||||
/utils/browser/playwright-web.js
|
||||
/index.d.ts
|
||||
|
|
|
@ -26,7 +26,7 @@ try {
|
|||
}
|
||||
|
||||
(async function() {
|
||||
const {generateWebKitProtocol, generateChromeProtocol} = require('./utils/protocol-types-generator/') ;
|
||||
const {generateWebKitProtocol, generateFirefoxProtocol, generateChromeProtocol} = require('./utils/protocol-types-generator/') ;
|
||||
try {
|
||||
const chromeRevision = await downloadBrowser('chromium', require('./chromium').createBrowserFetcher());
|
||||
await generateChromeProtocol(chromeRevision);
|
||||
|
@ -35,7 +35,8 @@ try {
|
|||
}
|
||||
|
||||
try {
|
||||
await downloadBrowser('firefox', require('./firefox').createBrowserFetcher());
|
||||
const firefoxRevision = await downloadBrowser('firefox', require('./firefox').createBrowserFetcher());
|
||||
await generateFirefoxProtocol(firefoxRevision);
|
||||
} catch (e) {
|
||||
console.warn(e.message);
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
"jpeg-js": "^0.3.4",
|
||||
"minimist": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"node-stream-zip": "^1.8.2",
|
||||
"pixelmatch": "^4.0.2",
|
||||
"pngjs": "^3.3.3",
|
||||
"text-diff": "^1.0.1",
|
||||
|
|
|
@ -19,6 +19,7 @@ import {assert} from '../helper';
|
|||
import {EventEmitter} from 'events';
|
||||
import * as debug from 'debug';
|
||||
import { ConnectionTransport } from '../ConnectionTransport';
|
||||
import { Protocol } from './protocol';
|
||||
const debugProtocol = debug('playwright:protocol');
|
||||
|
||||
export const ConnectionEvents = {
|
||||
|
@ -144,6 +145,12 @@ export class JugglerSession extends EventEmitter {
|
|||
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
|
||||
private _targetType: string;
|
||||
private _sessionId: string;
|
||||
on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
addListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
off: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
removeListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
once: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||
|
||||
constructor(connection: Connection, targetType: string, sessionId: string) {
|
||||
super();
|
||||
this._callbacks = new Map();
|
||||
|
@ -152,7 +159,10 @@ export class JugglerSession extends EventEmitter {
|
|||
this._sessionId = sessionId;
|
||||
}
|
||||
|
||||
send(method: string, params: any = {}): Promise<any> {
|
||||
send<T extends keyof Protocol.CommandParameters>(
|
||||
method: T,
|
||||
params?: Protocol.CommandParameters[T]
|
||||
): Promise<Protocol.CommandReturnValues[T]> {
|
||||
if (!this._connection)
|
||||
return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`));
|
||||
const id = this._connection._rawSend({sessionId: this._sessionId, method, params});
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
import {helper, debugError} from '../helper';
|
||||
import * as js from '../javascript';
|
||||
import { JugglerSession } from './Connection';
|
||||
import { Protocol } from './protocol';
|
||||
|
||||
export class ExecutionContextDelegate implements js.ExecutionContextDelegate {
|
||||
_session: JugglerSession;
|
||||
|
@ -104,7 +105,7 @@ export class ExecutionContextDelegate implements js.ExecutionContextDelegate {
|
|||
checkException(payload.exceptionDetails);
|
||||
return context._createHandle(payload.result);
|
||||
|
||||
function rewriteError(error) {
|
||||
function rewriteError(error) : never {
|
||||
if (error.message.includes('Failed to find execution context with id'))
|
||||
throw new Error('Execution context was destroyed, most likely because of a navigation.');
|
||||
throw error;
|
||||
|
@ -167,7 +168,7 @@ function checkException(exceptionDetails?: any) {
|
|||
}
|
||||
}
|
||||
|
||||
export function deserializeValue({unserializableValue, value}) {
|
||||
export function deserializeValue({unserializableValue, value}: Protocol.RemoteObject) {
|
||||
if (unserializableValue === 'Infinity')
|
||||
return Infinity;
|
||||
if (unserializableValue === '-Infinity')
|
||||
|
|
|
@ -189,7 +189,7 @@ class InterceptableRequest {
|
|||
(this.request as any)[interceptableRequestSymbol] = this;
|
||||
}
|
||||
|
||||
async continue(overrides: any = {}) {
|
||||
async continue(overrides: {url?: string, method?: string, postData?: string, headers?: {[key: string]: string}} = {}) {
|
||||
assert(!overrides.url, 'Playwright-Firefox does not support overriding URL');
|
||||
assert(!overrides.method, 'Playwright-Firefox does not support overriding method');
|
||||
assert(!overrides.postData, 'Playwright-Firefox does not support overriding postData');
|
||||
|
|
|
@ -118,7 +118,7 @@ export class Page extends EventEmitter {
|
|||
}
|
||||
|
||||
async emulateMedia(options: {
|
||||
type?: string,
|
||||
type?: ""|"screen"|"print",
|
||||
colorScheme?: 'dark' | 'light' | 'no-preference' }) {
|
||||
assert(!options.type || input.mediaTypes.has(options.type), 'Unsupported media type: ' + options.type);
|
||||
assert(!options.colorScheme || input.mediaColorSchemes.has(options.colorScheme), 'Unsupported color scheme: ' + options.colorScheme);
|
||||
|
|
|
@ -97,6 +97,11 @@ module.exports.addTests = function({testRunner, expect, CHROME, FFOX, WEBKIT}) {
|
|||
else if (FFOX)
|
||||
expect(error.message).toContain('Object is not serializable');
|
||||
});
|
||||
it('should work with tricky values', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({a: 1}));
|
||||
const json = await aHandle.jsonValue();
|
||||
expect(json).toEqual({a: 1});
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSHandle.getProperties', function() {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// @ts-check
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const StreamZip = require('node-stream-zip');
|
||||
const vm = require('vm');
|
||||
|
||||
async function generateChromeProtocol(revision) {
|
||||
const outputPath = path.join(__dirname, '..', '..', 'src', 'chromium', 'protocol.d.ts');
|
||||
|
@ -114,4 +116,99 @@ function typeOfProperty(property, domain) {
|
|||
return property.type;
|
||||
}
|
||||
|
||||
module.exports = {generateChromeProtocol, generateWebKitProtocol};
|
||||
async function generateFirefoxProtocol(revision) {
|
||||
const outputPath = path.join(__dirname, '..', '..', 'src', 'firefox', 'protocol.d.ts');
|
||||
if (revision.local && fs.existsSync(outputPath))
|
||||
return;
|
||||
const zip = new StreamZip({file: path.join(revision.executablePath, '..', 'omni.ja'), storeEntries: true});
|
||||
// @ts-ignore
|
||||
await new Promise(x => zip.on('ready', x));
|
||||
const data = zip.entryDataSync(zip.entry('chrome/juggler/content/protocol/Protocol.js'))
|
||||
|
||||
const ctx = vm.createContext();
|
||||
const protocolJSCode = data.toString('utf8');
|
||||
function inject() {
|
||||
this.ChromeUtils = {
|
||||
import: () => ({t})
|
||||
}
|
||||
const t = {};
|
||||
t.String = {"$type": "string"};
|
||||
t.Number = {"$type": "number"};
|
||||
t.Boolean = {"$type": "boolean"};
|
||||
t.Undefined = {"$type": "undefined"};
|
||||
t.Any = {"$type": "any"};
|
||||
|
||||
t.Enum = function(values) {
|
||||
return {"$type": "enum", "$values": values};
|
||||
}
|
||||
|
||||
t.Nullable = function(scheme) {
|
||||
return {...scheme, "$nullable": true};
|
||||
}
|
||||
|
||||
t.Optional = function(scheme) {
|
||||
return {...scheme, "$optional": true};
|
||||
}
|
||||
|
||||
t.Array = function(scheme) {
|
||||
return {"$type": "array", "$items": scheme};
|
||||
}
|
||||
|
||||
t.Recursive = function(types, schemeName) {
|
||||
return {"$type": "ref", "$ref": schemeName };
|
||||
}
|
||||
}
|
||||
const json = vm.runInContext(`(${inject})();${protocolJSCode}; this.protocol.types = types; this.protocol;`, ctx);
|
||||
fs.writeFileSync(outputPath, firefoxJSONToTS(json));
|
||||
console.log(`Wrote protocol.d.ts for Firefox to ${path.relative(process.cwd(), outputPath)}`);
|
||||
}
|
||||
|
||||
function firefoxJSONToTS(json) {
|
||||
const domains = Object.entries(json.domains);
|
||||
return `// This is generated from /utils/protocol-types-generator/index.js
|
||||
export module Protocol {${Object.entries(json.types).map(([typeName, type]) => `
|
||||
export type ${typeName} = ${firefoxTypeToString(type, ' ')};`).join('')}
|
||||
${domains.map(([domainName, domain]) => `
|
||||
export module ${domainName} {${(Object.entries(domain.events)).map(([eventName, event]) => `
|
||||
export type ${eventName}Payload = ${firefoxTypeToString(event)}`).join('')}${(Object.entries(domain.methods)).map(([commandName, command]) => `
|
||||
export type ${commandName}Parameters = ${firefoxTypeToString(command.params)};
|
||||
export type ${commandName}ReturnValue = ${firefoxTypeToString(command.returns)};`).join('')}
|
||||
}`).join('')}
|
||||
export interface Events {${domains.map(([domainName, domain]) => Object.keys(domain.events).map(eventName => `
|
||||
"${domainName}.${eventName}": ${domainName}.${eventName}Payload;`).join('')).join('')}
|
||||
}
|
||||
export interface CommandParameters {${domains.map(([domainName, domain]) => Object.keys(domain.methods).map(commandName => `
|
||||
"${domainName}.${commandName}": ${domainName}.${commandName}Parameters;`).join('')).join('')}
|
||||
}
|
||||
export interface CommandReturnValues {${domains.map(([domainName, domain]) => Object.keys(domain.methods).map(commandName => `
|
||||
"${domainName}.${commandName}": ${domainName}.${commandName}ReturnValue;`).join('')).join('')}
|
||||
}
|
||||
}`
|
||||
|
||||
}
|
||||
|
||||
function firefoxTypeToString(type, indent=' ') {
|
||||
if (!type)
|
||||
return 'void';
|
||||
if (!type['$type']) {
|
||||
const properties = Object.entries(type).filter(([name]) => !name.startsWith('$'));
|
||||
const lines = [];
|
||||
lines.push('{');
|
||||
for (const [propertyName, property] of properties) {
|
||||
const nameSuffix = property['$optional'] ? '?' : '';
|
||||
const valueSuffix = property['$nullable'] ? '|null' : ''
|
||||
lines.push(`${indent} ${propertyName}${nameSuffix}: ${firefoxTypeToString(property, indent + ' ')}${valueSuffix};`);
|
||||
}
|
||||
lines.push(`${indent}}`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
if (type['$type'] === 'ref')
|
||||
return type['$ref'];
|
||||
if (type['$type'] === 'array')
|
||||
return firefoxTypeToString(type['$items'], indent) + '[]';
|
||||
if (type['$type'] === 'enum')
|
||||
return type['$values'].map(v => JSON.stringify(v)).join('|');
|
||||
return type['$type'];
|
||||
}
|
||||
|
||||
module.exports = {generateChromeProtocol, generateFirefoxProtocol, generateWebKitProtocol};
|
Loading…
Reference in New Issue