chore: migrate src/Connection to TypeScript (#5694)

* chore: migrate `src/Connection` to TypeScript

This commit migrates `src/Connection` to TypeScript. It also changes its
exports to be ESM because TypeScript's support for exporting values to
use as types via CommonJS is poor (by design) and so rather than battle
that it made more sense to migrate the file to ESM.

The good news is that TypeScript is still outputting to `lib/` as
CommonJS, so the fact that we author in ESM is actually not a breaking
change at all.

So going forwards we will:

* migrate TS files to use ESM for importing and exporting
* continue to output to `lib/` as CommonJS
* continue to use CommonJS requires when in a `src/*.js` file

I'd also like to split `Connection.ts` into two; I think the
`CDPSession` class belongs in its own file, but I will do that in
another PR to avoid this one becoming bigger than it already is.

I also turned off `@typescript-eslint/no-use-before-define` as I don't
think it was adding value and Puppeteer's codebase seems to have a style
of declaring helper functions at the bottom which is fine by me.

Finally, I updated the DocLint tool so it knows of expected method
mismatches. It was either that or come up with a smart way to support
TypeScript generics in DocLint and given we don't want to use DocLint
that much longer that didn't feel worth it.

* Fix params being required
This commit is contained in:
Jack Franklin 2020-04-21 09:20:25 +01:00 committed by GitHub
parent 376d234ed1
commit a614bc45aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 188 additions and 108 deletions

View File

@ -112,7 +112,8 @@ module.exports = {
"@typescript-eslint/no-unused-vars": 2,
"semi": 0,
"@typescript-eslint/semi": 2,
"@typescript-eslint/no-empty-function": 0
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/no-use-before-define": 0
}
}
]

View File

@ -14,6 +14,10 @@
* limitations under the License.
*/
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
/**
* @typedef {Object} SerializedAXNode
* @property {string} role
@ -53,7 +57,7 @@
class Accessibility {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
*/
constructor(client) {
this._client = client;

View File

@ -19,10 +19,13 @@ const {Target} = require('./Target');
const EventEmitter = require('events');
const {TaskQueue} = require('./TaskQueue');
const {Events} = require('./Events');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {Connection} = require('./Connection');
class Browser extends EventEmitter {
/**
* @param {!Puppeteer.Connection} connection
* @param {!Connection} connection
* @param {!Array<string>} contextIds
* @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport
@ -36,7 +39,7 @@ class Browser extends EventEmitter {
}
/**
* @param {!Puppeteer.Connection} connection
* @param {!Connection} connection
* @param {!Array<string>} contextIds
* @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport
@ -277,7 +280,7 @@ class Browser extends EventEmitter {
class BrowserContext extends EventEmitter {
/**
* @param {!Puppeteer.Connection} connection
* @param {!Connection} connection
* @param {!Browser} browser
* @param {?string} contextId
*/

View File

@ -13,38 +13,51 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {assert} = require('./helper');
const {Events} = require('./Events');
const debugProtocol = require('debug')('puppeteer:protocol');
const EventEmitter = require('events');
import helper = require('./helper');
const {assert} = helper;
import EventsModule = require('./Events');
const {Events} = EventsModule;
import debug = require('debug');
const debugProtocol = debug('puppeteer:protocol');
import EventEmitter = require('events');
interface ConnectionCallback {
resolve: Function;
reject: Function;
error: Error;
method: string;
}
export class Connection extends EventEmitter {
_url: string;
_transport: Puppeteer.ConnectionTransport;
_delay: number;
_lastId = 0;
_sessions: Map<string, CDPSession> = new Map();
_closed = false;
_callbacks: Map<number, ConnectionCallback> = new Map();
class Connection extends EventEmitter {
/**
* @param {string} url
* @param {!Puppeteer.ConnectionTransport} transport
* @param {number=} delay
*/
constructor(url, transport, delay = 0) {
constructor(url: string, transport: Puppeteer.ConnectionTransport, delay = 0) {
super();
this._url = url;
this._lastId = 0;
/** @type {!Map<number, {resolve: function, reject: function, error: !Error, method: string}>}*/
this._callbacks = new Map();
this._delay = delay;
this._transport = transport;
this._transport.onmessage = this._onMessage.bind(this);
this._transport.onclose = this._onClose.bind(this);
/** @type {!Map<string, !CDPSession>}*/
this._sessions = new Map();
this._closed = false;
}
/**
* @param {!CDPSession} session
* @return {!Connection}
*/
static fromSession(session) {
static fromSession(session: CDPSession): Connection {
return session._connection;
}
@ -52,34 +65,22 @@ class Connection extends EventEmitter {
* @param {string} sessionId
* @return {?CDPSession}
*/
session(sessionId) {
session(sessionId: string): CDPSession | null {
return this._sessions.get(sessionId) || null;
}
/**
* @return {string}
*/
url() {
url(): string {
return this._url;
}
/**
* @param {string} method
* @param {!Object=} params
* @return {!Promise<?Object>}
*/
send(method, params = {}) {
send<ReturnType extends {}>(method: string, params = {}): Promise<ReturnType> {
const id = this._rawSend({method, params});
return new Promise((resolve, reject) => {
this._callbacks.set(id, {resolve, reject, error: new Error(), method});
});
}
/**
* @param {*} message
* @return {number}
*/
_rawSend(message) {
_rawSend(message: {}): number {
const id = ++this._lastId;
message = JSON.stringify(Object.assign({}, message, {id}));
debugProtocol('SEND ► ' + message);
@ -87,10 +88,7 @@ class Connection extends EventEmitter {
return id;
}
/**
* @param {string} message
*/
async _onMessage(message) {
async _onMessage(message: string): Promise<void> {
if (this._delay)
await new Promise(f => setTimeout(f, this._delay));
debugProtocol('◀ RECV ' + message);
@ -125,7 +123,7 @@ class Connection extends EventEmitter {
}
}
_onClose() {
_onClose(): void {
if (this._closed)
return;
this._closed = true;
@ -140,7 +138,7 @@ class Connection extends EventEmitter {
this.emit(Events.Connection.Disconnected);
}
dispose() {
dispose(): void {
this._onClose();
this._transport.close();
}
@ -149,45 +147,53 @@ class Connection extends EventEmitter {
* @param {Protocol.Target.TargetInfo} targetInfo
* @return {!Promise<!CDPSession>}
*/
async createSession(targetInfo) {
const {sessionId} = await this.send('Target.attachToTarget', {targetId: targetInfo.targetId, flatten: true});
async createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CDPSession> {
const {sessionId} = await this.send<Protocol.Target.attachedToTargetPayload>('Target.attachToTarget', {targetId: targetInfo.targetId, flatten: true});
return this._sessions.get(sessionId);
}
}
class CDPSession extends EventEmitter {
/**
* @param {!Connection} connection
* @param {string} targetType
* @param {string} sessionId
*/
constructor(connection, targetType, sessionId) {
interface CDPSessionOnMessageObject {
id?: number;
method: string;
params: {};
error: {message: string; data: any};
result?: any;
}
export class CDPSession extends EventEmitter {
_connection: Connection;
_sessionId: string;
_targetType: string;
_callbacks: Map<number, ConnectionCallback> = new Map();
constructor(connection: Connection, targetType: string, sessionId: string) {
super();
/** @type {!Map<number, {resolve: function, reject: function, error: !Error, method: string}>}*/
this._callbacks = new Map();
this._connection = connection;
this._targetType = targetType;
this._sessionId = sessionId;
}
/**
* @param {string} method
* @param {!Object=} params
* @return {!Promise<?Object>}
*/
send(method, params = {}) {
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});
const id = this._connection._rawSend({
sessionId: this._sessionId,
method,
/* TODO(jacktfranklin@): once this Firefox bug is solved
* we no longer need the `|| {}` check
* https://bugzilla.mozilla.org/show_bug.cgi?id=1631570
*/
params: params || {}
});
return new Promise((resolve, reject) => {
this._callbacks.set(id, {resolve, reject, error: new Error(), method});
});
}
/**
* @param {{id?: number, method: string, params: Object, error: {message: string, data: any}, result?: *}} object
*/
_onMessage(object) {
_onMessage(object: CDPSessionOnMessageObject): void {
if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id);
@ -201,13 +207,13 @@ class CDPSession extends EventEmitter {
}
}
async detach() {
async detach(): Promise<void> {
if (!this._connection)
throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`);
await this._connection.send('Target.detachFromTarget', {sessionId: this._sessionId});
}
_onClosed() {
_onClosed(): void {
for (const callback of this._callbacks.values())
callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`));
this._callbacks.clear();
@ -222,7 +228,7 @@ class CDPSession extends EventEmitter {
* @param {{error: {message: string, data: any}}} object
* @return {!Error}
*/
function createProtocolError(error, method, object) {
function createProtocolError(error: Error, method: string, object: { error: { message: string; data: any}}): Error {
let message = `Protocol error (${method}): ${object.error.message}`;
if ('data' in object.error)
message += ` ${object.error.data}`;
@ -234,9 +240,7 @@ function createProtocolError(error, method, object) {
* @param {string} message
* @return {!Error}
*/
function rewriteError(error, message) {
function rewriteError(error: Error, message: string): Error {
error.message = message;
return error;
}
module.exports = {Connection, CDPSession};

View File

@ -15,6 +15,9 @@
*/
const {helper, debugError, assert} = require('./helper');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
const {EVALUATION_SCRIPT_URL} = require('./ExecutionContext');
@ -27,7 +30,7 @@ const {EVALUATION_SCRIPT_URL} = require('./ExecutionContext');
class Coverage {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
*/
constructor(client) {
this._jsCoverage = new JSCoverage(client);
@ -67,7 +70,7 @@ module.exports = {Coverage};
class JSCoverage {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
*/
constructor(client) {
this._client = client;
@ -166,7 +169,7 @@ class JSCoverage {
class CSSCoverage {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
*/
constructor(client) {
this._client = client;

View File

@ -15,6 +15,7 @@
*/
import helpers = require('./helper');
import {CDPSession} from './Connection';
const {assert} = helpers;
@ -28,13 +29,13 @@ enum DialogType {
class Dialog {
static Type = DialogType;
private _client: Puppeteer.CDPSession;
private _client: CDPSession;
private _type: DialogType;
private _message: string;
private _defaultValue: string;
private _handled = false;
constructor(client: Puppeteer.CDPSession, type: DialogType, message: string, defaultValue = '') {
constructor(client: CDPSession, type: DialogType, message: string, defaultValue = '') {
this._client = client;
this._type = type;
this._message = message;

View File

@ -14,9 +14,13 @@
* limitations under the License.
*/
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
class EmulationManager {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
*/
constructor(client) {
this._client = client;

View File

@ -16,13 +16,16 @@
const {helper, assert} = require('./helper');
const {createJSHandle, JSHandle} = require('./JSHandle');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
const EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__';
const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
class ExecutionContext {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {!Protocol.Runtime.ExecutionContextDescription} contextPayload
* @param {?Puppeteer.DOMWorld} world
*/

View File

@ -24,12 +24,15 @@ const {NetworkManager} = require('./NetworkManager');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {TimeoutSettings} = require('./TimeoutSettings');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
class FrameManager extends EventEmitter {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {!Puppeteer.Page} page
* @param {boolean} ignoreHTTPSErrors
* @param {!TimeoutSettings} timeoutSettings
@ -112,7 +115,7 @@ class FrameManager extends EventEmitter {
return watcher.navigationResponse();
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {string} url
* @param {string} referrer
* @param {string} frameId
@ -376,7 +379,7 @@ class FrameManager extends EventEmitter {
class Frame {
/**
* @param {!FrameManager} frameManager
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {?Frame} parentFrame
* @param {string} frameId
*/

View File

@ -16,6 +16,9 @@
const {assert} = require('./helper');
const keyDefinitions = require('./USKeyboardLayout');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
/**
* @typedef {Object} KeyDescription
@ -28,7 +31,7 @@ const keyDefinitions = require('./USKeyboardLayout');
class Keyboard {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
*/
constructor(client) {
this._client = client;
@ -183,7 +186,7 @@ class Keyboard {
class Mouse {
/**
* @param {Puppeteer.CDPSession} client
* @param {CDPSession} client
* @param {!Keyboard} keyboard
*/
constructor(client, keyboard) {
@ -274,7 +277,7 @@ class Mouse {
class Touchscreen {
/**
* @param {Puppeteer.CDPSession} client
* @param {CDPSession} client
* @param {Keyboard} keyboard
*/
constructor(client, keyboard) {

View File

@ -15,6 +15,9 @@
*/
const {helper, assert, debugError} = require('./helper');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
function createJSHandle(context, remoteObject) {
const frame = context.frame();
@ -28,7 +31,7 @@ function createJSHandle(context, remoteObject) {
class JSHandle {
/**
* @param {!Puppeteer.ExecutionContext} context
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {!Protocol.Runtime.RemoteObject} remoteObject
*/
constructor(context, client, remoteObject) {
@ -142,7 +145,7 @@ class JSHandle {
class ElementHandle extends JSHandle {
/**
* @param {!Puppeteer.ExecutionContext} context
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {!Protocol.Runtime.RemoteObject} remoteObject
* @param {!Puppeteer.Page} page
* @param {!Puppeteer.FrameManager} frameManager

View File

@ -16,10 +16,13 @@
const EventEmitter = require('events');
const {helper, assert, debugError} = require('./helper');
const {Events} = require('./Events');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
class NetworkManager extends EventEmitter {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {!Puppeteer.FrameManager} frameManager
*/
constructor(client, ignoreHTTPSErrors, frameManager) {
@ -312,7 +315,7 @@ class NetworkManager extends EventEmitter {
class Request {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {?Puppeteer.Frame} frame
* @param {string} interceptionId
* @param {boolean} allowInterception
@ -524,7 +527,7 @@ const errorReasons = {
class Response {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {!Request} request
* @param {!Protocol.Network.Response} responsePayload
*/

View File

@ -18,7 +18,9 @@ const fs = require('fs');
const EventEmitter = require('events');
const mime = require('mime');
const {Events} = require('./Events');
const {Connection} = require('./Connection');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {Connection, CDPSession} = require('./Connection');
const {Dialog} = require('./Dialog');
const {EmulationManager} = require('./EmulationManager');
const {FrameManager} = require('./FrameManager');
@ -39,7 +41,7 @@ const writeFileAsync = helper.promisify(fs.writeFile);
class Page extends EventEmitter {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {!Puppeteer.Target} target
* @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport
@ -55,7 +57,7 @@ class Page extends EventEmitter {
}
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
* @param {!Puppeteer.Target} target
* @param {boolean} ignoreHTTPSErrors
* @param {!TaskQueue} screenshotTaskQueue
@ -1361,7 +1363,7 @@ class ConsoleMessage {
class FileChooser {
/**
* @param {Puppeteer.CDPSession} client
* @param {CDPSession} client
* @param {Puppeteer.ElementHandle} element
* @param {!Protocol.Page.fileChooserOpenedPayload} event
*/

View File

@ -17,6 +17,9 @@
const {Events} = require('./Events');
const {Page} = require('./Page');
const {Worker: PuppeteerWorker} = require('./Worker');
// CDPSession is used only as a typedef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
// This import is used as a TypeDef, but ESLint's rule doesn't
// understand that unfortunately.
// eslint-disable-next-line no-unused-vars
@ -26,7 +29,7 @@ class Target {
/**
* @param {!Protocol.Target.TargetInfo} targetInfo
* @param {!Puppeteer.BrowserContext} browserContext
* @param {!function():!Promise<!Puppeteer.CDPSession>} sessionFactory
* @param {!function():!Promise<!CDPSession>} sessionFactory
* @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport
* @param {!TaskQueue} screenshotTaskQueue
@ -63,7 +66,7 @@ class Target {
}
/**
* @return {!Promise<!Puppeteer.CDPSession>}
* @return {!Promise<!CDPSession>}
*/
createCDPSession() {
return this._sessionFactory();

View File

@ -14,10 +14,13 @@
* limitations under the License.
*/
const {helper, assert} = require('./helper');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
class Tracing {
/**
* @param {!Puppeteer.CDPSession} client
* @param {!CDPSession} client
*/
constructor(client) {
this._client = client;

View File

@ -17,10 +17,13 @@ const EventEmitter = require('events');
const {debugError} = require('./helper');
const {ExecutionContext} = require('./ExecutionContext');
const {JSHandle} = require('./JSHandle');
// Used as a TypeDef
// eslint-disable-next-line no-unused-vars
const {CDPSession} = require('./Connection');
class Worker extends EventEmitter {
/**
* @param {Puppeteer.CDPSession} client
* @param {CDPSession} client
* @param {string} url
* @param {function(string, !Array<!JSHandle>, Protocol.Runtime.StackTrace=):void} consoleAPICalled
* @param {function(!Protocol.Runtime.ExceptionDetails):void} exceptionThrown

6
src/externs.d.ts vendored
View File

@ -1,4 +1,3 @@
import { Connection as RealConnection, CDPSession as RealCDPSession } from './Connection.js';
import { Browser as RealBrowser, BrowserContext as RealBrowserContext} from './Browser.js';
import {Target as RealTarget} from './Target.js';
import {Page as RealPage} from './Page.js';
@ -11,11 +10,6 @@ import { NetworkManager as RealNetworkManager, Request as RealRequest, Response
import * as child_process from 'child_process';
declare global {
module Puppeteer {
export class Connection extends RealConnection {}
export class CDPSession extends RealCDPSession {
on<T extends keyof Protocol.Events>(event: T, listener: (arg: Protocol.Events[T]) => void): this;
send<T extends keyof Protocol.CommandParameters>(message: T, parameters?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T]>;
}
export class Mouse extends RealMouse {}
export class Keyboard extends RealKeyboard {}
export class Touchscreen extends RealTouchscreen {}

View File

@ -19,6 +19,7 @@ import debug = require('debug');
const debugError = debug('puppeteer:error');
import fs = require('fs');
import promisify = require('./promisify');
import {CDPSession} from './Connection';
const {TimeoutError} = Errors;
@ -71,7 +72,7 @@ function valueFromRemoteObject(remoteObject: Protocol.Runtime.RemoteObject): unk
return remoteObject.value;
}
async function releaseObject(client: Puppeteer.CDPSession, remoteObject: Protocol.Runtime.RemoteObject): Promise<void> {
async function releaseObject(client: CDPSession, remoteObject: Protocol.Runtime.RemoteObject): Promise<void> {
if (!remoteObject.objectId)
return;
await client.send('Runtime.releaseObject', {objectId: remoteObject.objectId}).catch(error => {
@ -185,7 +186,7 @@ async function waitWithTimeout<T extends any>(promise: Promise<T>, taskName: str
}
}
async function readProtocolStream(client: Puppeteer.CDPSession, handle: string, path?: string): Promise<Buffer> {
async function readProtocolStream(client: CDPSession, handle: string, path?: string): Promise<Buffer> {
let eof = false;
let file;
if (path)

View File

@ -247,6 +247,41 @@ function compareDocumentations(actual, expected) {
checkType(source + ' ' + actual.name, actual.type, expected.type);
}
/**
* @param {string} source
* @param {!string} actualName
* @param {!string} expectedName
*/
function namingMisMatchInTypeIsExpected(source, actualName, expectedName) {
/* The DocLint tooling doesn't deal well with generics in TypeScript
* source files. We could fix this but the longterm plan is to
* auto-generate documentation from TS. So instead we document here
* the methods that use generics that DocLint trips up on and if it
* finds a mismatch that matches one of the cases below it doesn't
* error. This still means we're protected from accidental changes, as
* if the mismatch doesn't exactly match what's described below
* DocLint will fail.
*/
const expectedNamingMismatches = new Map([
['Method CDPSession.send() method', {
actualName: 'string',
expectedName: 'T'
}],
['Method CDPSession.send() params', {
actualName: 'Object',
expectedName: 'CommandParameters[T]'
}],
]);
const expectedForSource = expectedNamingMismatches.get(source);
if (!expectedForSource) return false;
const namingMismatchIsExpected = expectedForSource.actualName === actualName && expectedForSource.expectedName === expectedName;
return namingMismatchIsExpected;
}
/**
* @param {string} source
* @param {!Documentation.Type} actual
@ -260,8 +295,12 @@ function compareDocumentations(actual, expected) {
const actualName = actual.name.replace(/[\? ]/g, '');
// TypeScript likes to add some spaces
const expectedName = expected.name.replace(/\ /g, '');
if (expectedName !== actualName)
errors.push(`${source} ${actualName} != ${expectedName}`);
if (expectedName !== actualName) {
const namingMismatchIsExpected = namingMisMatchInTypeIsExpected(source, actualName, expectedName);
if (!namingMismatchIsExpected)
errors.push(`${source} ${actualName} != ${expectedName}`);
}
const actualPropertiesMap = new Map(actual.properties.map(property => [property.name, property.type]));
const expectedPropertiesMap = new Map(expected.properties.map(property => [property.name, property.type]));
const propertiesDiff = diff(Array.from(actualPropertiesMap.keys()).sort(), Array.from(expectedPropertiesMap.keys()).sort());