@applitools/eyes-cypress 3.24.0-beta.5 → 3.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +2191 -2195
  2. package/LICENSE +25 -25
  3. package/README.md +803 -782
  4. package/bin/eyes-setup.js +21 -21
  5. package/commands.js +2 -2
  6. package/dist/browser/spec-driver.js +110 -106
  7. package/dist/plugin/handler.js +55 -55
  8. package/eyes-index.d.ts +34 -34
  9. package/index.js +2 -2
  10. package/package.json +98 -97
  11. package/src/browser/commands.js +188 -188
  12. package/src/browser/eyesCheckMapping.js +104 -103
  13. package/src/browser/eyesOpenMapping.js +60 -60
  14. package/src/browser/makeSend.js +18 -18
  15. package/src/browser/refer.js +57 -57
  16. package/src/browser/sendRequest.js +16 -16
  17. package/src/browser/socket.js +144 -143
  18. package/src/browser/socketCommands.js +81 -81
  19. package/src/browser/spec-driver.ts +117 -111
  20. package/src/pem/server.cert +22 -22
  21. package/src/pem/server.key +27 -27
  22. package/src/plugin/concurrencyMsg.js +8 -8
  23. package/src/plugin/config.js +54 -54
  24. package/src/plugin/defaultPort.js +1 -1
  25. package/src/plugin/errorDigest.js +96 -96
  26. package/src/plugin/getErrorsAndDiffs.js +34 -34
  27. package/src/plugin/handleTestResults.js +35 -39
  28. package/src/plugin/handler.ts +58 -58
  29. package/src/plugin/hooks.js +51 -50
  30. package/src/plugin/isGlobalHooksSupported.js +13 -13
  31. package/src/plugin/pluginExport.js +60 -60
  32. package/src/plugin/server.js +98 -98
  33. package/src/plugin/startPlugin.js +15 -13
  34. package/src/plugin/webSocket.js +130 -130
  35. package/src/setup/addEyesCommands.js +24 -24
  36. package/src/setup/addEyesCypressPlugin.js +15 -15
  37. package/src/setup/getCypressConfig.js +16 -16
  38. package/src/setup/getFilePath.js +22 -22
  39. package/src/setup/handleCommands.js +23 -23
  40. package/src/setup/handlePlugin.js +23 -23
  41. package/src/setup/handleTypeScript.js +21 -21
  42. package/src/setup/isCommandsDefined.js +7 -7
  43. package/src/setup/isPluginDefined.js +7 -7
  44. package/test/fixtures/testAppCopies/.gitignore +1 -1
@@ -1,34 +1,34 @@
1
- 'use strict';
2
-
3
- function getErrorsAndDiffs(testResultsArr) {
4
- return testResultsArr.reduce(
5
- ({failed, diffs, passed}, testResults) => {
6
- if (testResults instanceof Error || testResults.error) {
7
- failed.push(testResults);
8
- } else {
9
- const testStatus = testResults.getStatus();
10
- if (testStatus === 'Passed') {
11
- passed.push(testResults);
12
- } else {
13
- if (testStatus === 'Unresolved') {
14
- if (testResults.getIsNew()) {
15
- testResults.error = new Error(
16
- `${testResults.getName()}. Please approve the new baseline at ${testResults.getUrl()}`,
17
- );
18
- failed.push(testResults);
19
- } else {
20
- diffs.push(testResults);
21
- }
22
- } else if (testStatus === 'Failed') {
23
- failed.push(testResults);
24
- }
25
- }
26
- }
27
-
28
- return {failed, diffs, passed};
29
- },
30
- {failed: [], diffs: [], passed: []},
31
- );
32
- }
33
-
34
- module.exports = getErrorsAndDiffs;
1
+ 'use strict';
2
+
3
+ function getErrorsAndDiffs(testResultsArr) {
4
+ return testResultsArr.reduce(
5
+ ({failed, diffs, passed}, testResults) => {
6
+ if (testResults instanceof Error || testResults.error) {
7
+ failed.push(testResults);
8
+ } else {
9
+ const testStatus = testResults.getStatus();
10
+ if (testStatus === 'Passed') {
11
+ passed.push(testResults);
12
+ } else {
13
+ if (testStatus === 'Unresolved') {
14
+ if (testResults.getIsNew()) {
15
+ testResults.error = new Error(
16
+ `${testResults.getName()}. Please approve the new baseline at ${testResults.getUrl()}`,
17
+ );
18
+ failed.push(testResults);
19
+ } else {
20
+ diffs.push(testResults);
21
+ }
22
+ } else if (testStatus === 'Failed') {
23
+ failed.push(testResults);
24
+ }
25
+ }
26
+ }
27
+
28
+ return {failed, diffs, passed};
29
+ },
30
+ {failed: [], diffs: [], passed: []},
31
+ );
32
+ }
33
+
34
+ module.exports = getErrorsAndDiffs;
@@ -1,39 +1,35 @@
1
- const errorDigest = require('./errorDigest');
2
- const {makeLogger} = require('@applitools/logger');
3
- const getErrorsAndDiffs = require('./getErrorsAndDiffs');
4
- const {promisify} = require('util');
5
- const fs = require('fs');
6
- const writeFile = promisify(fs.writeFile);
7
- const {TestResultsFormatter} = require('@applitools/visual-grid-client');
8
- const {resolve} = require('path');
9
-
10
- function printTestResults(testResultsArr) {
11
- const logger = makeLogger({
12
- level: testResultsArr.resultConfig.showLogs ? 'info' : 'silent',
13
- label: 'eyes',
14
- });
15
- const {passed, failed, diffs} = getErrorsAndDiffs(testResultsArr.testResults);
16
- if ((failed.length || diffs.length) && !!testResultsArr.resultConfig.eyesFailCypressOnDiff) {
17
- throw new Error(
18
- errorDigest({
19
- passed,
20
- failed,
21
- diffs,
22
- logger,
23
- isInteractive: !testResultsArr.resultConfig.isTextTerminal,
24
- }),
25
- );
26
- }
27
- }
28
- function handleBatchResultsFile(results, tapFileConfig) {
29
- try {
30
- const formatter = new TestResultsFormatter(results);
31
- const fileName = tapFileConfig.tapFileName || `${new Date().toISOString()}-eyes.tap`;
32
- const tapFile = resolve(tapFileConfig.tapDirPath, fileName);
33
- return writeFile(tapFile, formatter.asHierarchicTAPString(false, true));
34
- } catch (ex) {
35
- console.log(ex);
36
- }
37
- }
38
-
39
- module.exports = {printTestResults, handleBatchResultsFile};
1
+ const errorDigest = require('./errorDigest');
2
+ const {makeLogger} = require('@applitools/logger');
3
+ const getErrorsAndDiffs = require('./getErrorsAndDiffs');
4
+ const {promisify} = require('util');
5
+ const fs = require('fs');
6
+ const writeFile = promisify(fs.writeFile);
7
+ const {TestResultsFormatter} = require('@applitools/visual-grid-client');
8
+ const {resolve} = require('path');
9
+
10
+ function printTestResults(testResultsArr) {
11
+ const logger = makeLogger({
12
+ level: testResultsArr.resultConfig.showLogs ? 'info' : 'silent',
13
+ label: 'eyes',
14
+ });
15
+ const {passed, failed, diffs} = getErrorsAndDiffs(testResultsArr.testResults);
16
+ if ((failed.length || diffs.length) && !!testResultsArr.resultConfig.eyesFailCypressOnDiff) {
17
+ throw new Error(
18
+ errorDigest({
19
+ passed,
20
+ failed,
21
+ diffs,
22
+ logger,
23
+ isInteractive: !testResultsArr.resultConfig.isTextTerminal,
24
+ }),
25
+ );
26
+ }
27
+ }
28
+ function handleBatchResultsFile(results, tapFileConfig) {
29
+ const formatter = new TestResultsFormatter(results);
30
+ const fileName = tapFileConfig.tapFileName || `${new Date().toISOString()}-eyes.tap`;
31
+ const tapFile = resolve(tapFileConfig.tapDirPath, fileName);
32
+ return writeFile(tapFile, formatter.asHierarchicTAPString(false, true));
33
+ }
34
+
35
+ module.exports = {printTestResults, handleBatchResultsFile};
@@ -1,58 +1,58 @@
1
- import {Server as HTTPServer, request} from 'http'
2
- import {Server as WSServer} from 'ws'
3
-
4
- const {name, version} = require('../../package.json') // TODO is the handshake needed at all?
5
- const TOKEN_HEADER = 'x-eyes-universal-token'
6
- const TOKEN = `${name}@${version}`
7
-
8
- // TODO make default port 0 and return actual port
9
- export async function makeHandler({port = 31077, singleton = true, lazy = false} = {}): Promise<{
10
- server?: WSServer
11
- port: number
12
- }> {
13
- const http = new HTTPServer()
14
- http.on('request', (request, response) => {
15
- if (request.url === '/handshake') {
16
- if (request.headers[TOKEN_HEADER] === TOKEN) {
17
- response.writeHead(200, {[TOKEN_HEADER]: TOKEN})
18
- } else {
19
- response.writeHead(400)
20
- }
21
- response.end()
22
- }
23
- })
24
-
25
- http.listen(port, 'localhost')
26
-
27
- return new Promise((resolve, reject) => {
28
- http.on('listening', () => {
29
- const ws = new WSServer({server: http, path: '/eyes'})
30
- ws.on('close', () => http.close())
31
- resolve({server: ws, port})
32
- })
33
-
34
- http.on('error', async (err: Error & {code: string}) => {
35
- if (!lazy && err.code === 'EADDRINUSE') {
36
- if (singleton && (await isHandshakable(port))) {
37
- return resolve({port})
38
- } else {
39
- return resolve(await makeHandler({port: port + 1, singleton}))
40
- }
41
- }
42
- reject(err)
43
- })
44
- })
45
- }
46
-
47
- async function isHandshakable(port: number) {
48
- return new Promise(resolve => {
49
- const handshake = request(`http://localhost:${port}/handshake`, {
50
- headers: {[TOKEN_HEADER]: TOKEN},
51
- })
52
- handshake.on('response', ({statusCode, headers}) => {
53
- resolve(statusCode === 200 && headers[TOKEN_HEADER] === TOKEN)
54
- })
55
- handshake.on('error', () => resolve(false))
56
- handshake.end()
57
- })
58
- }
1
+ import {Server as HTTPServer, request} from 'http'
2
+ import {Server as WSServer} from 'ws'
3
+
4
+ const {name, version} = require('../../package.json') // TODO is the handshake needed at all?
5
+ const TOKEN_HEADER = 'x-eyes-universal-token'
6
+ const TOKEN = `${name}@${version}`
7
+
8
+ // TODO make default port 0 and return actual port
9
+ export async function makeHandler({port = 31077, singleton = true, lazy = false} = {}): Promise<{
10
+ server?: WSServer
11
+ port: number
12
+ }> {
13
+ const http = new HTTPServer()
14
+ http.on('request', (request, response) => {
15
+ if (request.url === '/handshake') {
16
+ if (request.headers[TOKEN_HEADER] === TOKEN) {
17
+ response.writeHead(200, {[TOKEN_HEADER]: TOKEN})
18
+ } else {
19
+ response.writeHead(400)
20
+ }
21
+ response.end()
22
+ }
23
+ })
24
+
25
+ http.listen(port, 'localhost')
26
+
27
+ return new Promise((resolve, reject) => {
28
+ http.on('listening', () => {
29
+ const ws = new WSServer({server: http, path: '/eyes'})
30
+ ws.on('close', () => http.close())
31
+ resolve({server: ws, port})
32
+ })
33
+
34
+ http.on('error', async (err: Error & {code: string}) => {
35
+ if (!lazy && err.code === 'EADDRINUSE') {
36
+ if (singleton && (await isHandshakable(port))) {
37
+ return resolve({port})
38
+ } else {
39
+ return resolve(await makeHandler({port: port + 1, singleton}))
40
+ }
41
+ }
42
+ reject(err)
43
+ })
44
+ })
45
+ }
46
+
47
+ async function isHandshakable(port: number) {
48
+ return new Promise(resolve => {
49
+ const handshake = request(`http://localhost:${port}/handshake`, {
50
+ headers: {[TOKEN_HEADER]: TOKEN},
51
+ })
52
+ handshake.on('response', ({statusCode, headers}) => {
53
+ resolve(statusCode === 200 && headers[TOKEN_HEADER] === TOKEN)
54
+ })
55
+ handshake.on('error', () => resolve(false))
56
+ handshake.end()
57
+ })
58
+ }
@@ -1,50 +1,51 @@
1
- 'use strict';
2
- const flatten = require('lodash.flatten');
3
- const {TestResults} = require('@applitools/visual-grid-client');
4
- const handleTestResults = require('./handleTestResults');
5
-
6
- function makeGlobalRunHooks({closeManager, closeBatches, closeUniversalServer}) {
7
- return {
8
- 'before:run': ({config}) => {
9
- if (!config.isTextTerminal) return;
10
- },
11
-
12
- 'after:run': async ({config}) => {
13
- try {
14
- if (!config.isTextTerminal) return;
15
- const resultConfig = {
16
- showLogs: config.showLogs,
17
- eyesFailCypressOnDiff: config.eyesFailCypressOnDiff,
18
- isTextTerminal: config.isTextTerminal,
19
- };
20
- const summary = await closeManager();
21
- const testResults = summary.results.map(({testResults}) => testResults);
22
- const testResultsArr = [];
23
- for (const result of flatten(testResults)) {
24
- testResultsArr.push(new TestResults(result));
25
- }
26
- if (!config.appliConfFile.dontCloseBatches) {
27
- await closeBatches({
28
- batchIds: [config.appliConfFile.batch.id],
29
- serverUrl: config.appliConfFile.serverUrl,
30
- proxy: config.appliConfFile.proxy,
31
- apiKey: config.appliConfFile.apiKey,
32
- });
33
- }
34
-
35
- if (config.appliConfFile.tapDirPath) {
36
- await handleTestResults.handleBatchResultsFile(testResultsArr, {
37
- tapDirPath: config.appliConfFile.tapDirPath,
38
- tapFileName: config.appliConfFile.tapFileName,
39
- });
40
- }
41
-
42
- handleTestResults.printTestResults({testResults: testResultsArr, resultConfig});
43
- } finally {
44
- closeUniversalServer();
45
- }
46
- },
47
- };
48
- }
49
-
50
- module.exports = makeGlobalRunHooks;
1
+ 'use strict';
2
+ const {TestResults} = require('@applitools/visual-grid-client');
3
+ const handleTestResults = require('./handleTestResults');
4
+
5
+ function makeGlobalRunHooks({closeManager, closeBatches, closeUniversalServer}) {
6
+ return {
7
+ 'before:run': ({config}) => {
8
+ if (!config.isTextTerminal) return;
9
+ },
10
+
11
+ 'after:run': async ({config}) => {
12
+ try {
13
+ if (!config.isTextTerminal) return;
14
+ const resultConfig = {
15
+ showLogs: config.showLogs,
16
+ eyesFailCypressOnDiff: config.eyesFailCypressOnDiff,
17
+ isTextTerminal: config.isTextTerminal,
18
+ };
19
+ const summaries = await closeManager();
20
+ const testResultsArr = [];
21
+ for (const summary of summaries) {
22
+ const testResults = summary.results.map(({testResults}) => testResults);
23
+ for (const result of testResults) {
24
+ testResultsArr.push(new TestResults(result));
25
+ }
26
+ }
27
+ if (!config.appliConfFile.dontCloseBatches) {
28
+ await closeBatches({
29
+ batchIds: [config.appliConfFile.batch.id],
30
+ serverUrl: config.appliConfFile.serverUrl,
31
+ proxy: config.appliConfFile.proxy,
32
+ apiKey: config.appliConfFile.apiKey,
33
+ });
34
+ }
35
+
36
+ if (config.appliConfFile.tapDirPath) {
37
+ await handleTestResults.handleBatchResultsFile(testResultsArr, {
38
+ tapDirPath: config.appliConfFile.tapDirPath,
39
+ tapFileName: config.appliConfFile.tapFileName,
40
+ });
41
+ }
42
+
43
+ handleTestResults.printTestResults({testResults: testResultsArr, resultConfig});
44
+ } finally {
45
+ await closeUniversalServer();
46
+ }
47
+ },
48
+ };
49
+ }
50
+
51
+ module.exports = makeGlobalRunHooks;
@@ -1,13 +1,13 @@
1
- const CYPRESS_SUPPORTED_VERSION = '6.2.0';
2
- const CYPRESS_NO_FLAG_VERSION = '6.7.0';
3
-
4
- function isGlobalHooksSupported(config) {
5
- const {version, experimentalRunEvents} = config;
6
-
7
- return (
8
- version >= CYPRESS_NO_FLAG_VERSION ||
9
- (version >= CYPRESS_SUPPORTED_VERSION && !!experimentalRunEvents)
10
- );
11
- }
12
-
13
- module.exports = isGlobalHooksSupported;
1
+ const CYPRESS_SUPPORTED_VERSION = '6.2.0';
2
+ const CYPRESS_NO_FLAG_VERSION = '6.7.0';
3
+
4
+ function isGlobalHooksSupported(config) {
5
+ const {version, experimentalRunEvents} = config;
6
+
7
+ return (
8
+ version >= CYPRESS_NO_FLAG_VERSION ||
9
+ (version >= CYPRESS_SUPPORTED_VERSION && !!experimentalRunEvents)
10
+ );
11
+ }
12
+
13
+ module.exports = isGlobalHooksSupported;
@@ -1,60 +1,60 @@
1
- 'use strict';
2
- const isGlobalHooksSupported = require('./isGlobalHooksSupported');
3
- const {presult} = require('@applitools/functional-commons');
4
- const makeGlobalRunHooks = require('./hooks');
5
-
6
- function makePluginExport({startServer, eyesConfig}) {
7
- return function pluginExport(pluginModule) {
8
- let eyesServer;
9
- const pluginModuleExports = pluginModule.exports;
10
- pluginModule.exports = async function(...args) {
11
- const {server, port, closeAllEyes, closeBatches, closeUniversalServer} = await startServer();
12
- eyesServer = server;
13
-
14
- const globalHooks = makeGlobalRunHooks({closeAllEyes, closeBatches, closeUniversalServer});
15
-
16
- const [origOn, config] = args;
17
- const isGlobalHookCalledFromUserHandlerMap = new Map();
18
- eyesConfig.eyesIsGlobalHooksSupported = isGlobalHooksSupported(config);
19
- const moduleExportsResult = await pluginModuleExports(onThatCallsUserDefinedHandler, config);
20
- if (eyesConfig.eyesIsGlobalHooksSupported) {
21
- for (const [eventName, eventHandler] of Object.entries(globalHooks)) {
22
- if (!isGlobalHookCalledFromUserHandlerMap.get(eventName)) {
23
- origOn.call(this, eventName, eventHandler);
24
- }
25
- }
26
- }
27
-
28
- return Object.assign({}, eyesConfig, {eyesPort: port}, moduleExportsResult);
29
-
30
- // This piece of code exists because at the point of writing, Cypress does not support multiple event handlers:
31
- // https://github.com/cypress-io/cypress/issues/5240#issuecomment-948277554
32
- // So we wrap Cypress' `on` function in order to wrap the user-defined handler. This way we can call our own handler
33
- // in addition to the user's handler
34
- function onThatCallsUserDefinedHandler(eventName, handler) {
35
- const isRunEvent = eventName === 'before:run' || eventName === 'after:run';
36
- let handlerToCall = handler;
37
- if (eyesConfig.eyesIsGlobalHooksSupported && isRunEvent) {
38
- handlerToCall = handlerThatCallsUserDefinedHandler;
39
- isGlobalHookCalledFromUserHandlerMap.set(eventName, true);
40
- }
41
- return origOn.call(this, eventName, handlerToCall);
42
-
43
- async function handlerThatCallsUserDefinedHandler() {
44
- const [err] = await presult(
45
- Promise.resolve(globalHooks[eventName].apply(this, arguments)),
46
- );
47
- await handler.apply(this, arguments);
48
- if (err) {
49
- throw err;
50
- }
51
- }
52
- }
53
- };
54
- return function getCloseServer() {
55
- return eyesServer.close();
56
- };
57
- };
58
- }
59
-
60
- module.exports = makePluginExport;
1
+ 'use strict';
2
+ const isGlobalHooksSupported = require('./isGlobalHooksSupported');
3
+ const {presult} = require('@applitools/functional-commons');
4
+ const makeGlobalRunHooks = require('./hooks');
5
+
6
+ function makePluginExport({startServer, eyesConfig}) {
7
+ return function pluginExport(pluginModule) {
8
+ let eyesServer;
9
+ const pluginModuleExports = pluginModule.exports;
10
+ pluginModule.exports = async function(...args) {
11
+ const {server, port, closeManager, closeBatches, closeUniversalServer} = await startServer();
12
+ eyesServer = server;
13
+
14
+ const globalHooks = makeGlobalRunHooks({closeManager, closeBatches, closeUniversalServer});
15
+
16
+ const [origOn, config] = args;
17
+ const isGlobalHookCalledFromUserHandlerMap = new Map();
18
+ eyesConfig.eyesIsGlobalHooksSupported = isGlobalHooksSupported(config);
19
+ const moduleExportsResult = await pluginModuleExports(onThatCallsUserDefinedHandler, config);
20
+ if (eyesConfig.eyesIsGlobalHooksSupported) {
21
+ for (const [eventName, eventHandler] of Object.entries(globalHooks)) {
22
+ if (!isGlobalHookCalledFromUserHandlerMap.get(eventName)) {
23
+ origOn.call(this, eventName, eventHandler);
24
+ }
25
+ }
26
+ }
27
+
28
+ return Object.assign({}, eyesConfig, {eyesPort: port}, moduleExportsResult);
29
+
30
+ // This piece of code exists because at the point of writing, Cypress does not support multiple event handlers:
31
+ // https://github.com/cypress-io/cypress/issues/5240#issuecomment-948277554
32
+ // So we wrap Cypress' `on` function in order to wrap the user-defined handler. This way we can call our own handler
33
+ // in addition to the user's handler
34
+ function onThatCallsUserDefinedHandler(eventName, handler) {
35
+ const isRunEvent = eventName === 'before:run' || eventName === 'after:run';
36
+ let handlerToCall = handler;
37
+ if (eyesConfig.eyesIsGlobalHooksSupported && isRunEvent) {
38
+ handlerToCall = handlerThatCallsUserDefinedHandler;
39
+ isGlobalHookCalledFromUserHandlerMap.set(eventName, true);
40
+ }
41
+ return origOn.call(this, eventName, handlerToCall);
42
+
43
+ async function handlerThatCallsUserDefinedHandler() {
44
+ const [err] = await presult(
45
+ Promise.resolve(globalHooks[eventName].apply(this, arguments)),
46
+ );
47
+ await handler.apply(this, arguments);
48
+ if (err) {
49
+ throw err;
50
+ }
51
+ }
52
+ }
53
+ };
54
+ return function getCloseServer() {
55
+ return eyesServer.close();
56
+ };
57
+ };
58
+ }
59
+
60
+ module.exports = makePluginExport;