@applitools/eyes-cypress 3.28.2 → 3.29.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 (40) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +28 -2
  3. package/dist/browser/spec-driver.js +0 -8
  4. package/dist/expose.js +7 -0
  5. package/dist/plugin/concurrencyMsg.js +9 -0
  6. package/dist/plugin/config.js +66 -0
  7. package/dist/plugin/configParams.js +47 -0
  8. package/dist/plugin/errorDigest.js +74 -0
  9. package/dist/plugin/getErrorsAndDiffs.js +31 -0
  10. package/dist/plugin/handleTestResults.js +38 -0
  11. package/dist/plugin/hooks.js +43 -0
  12. package/dist/plugin/index.js +17 -0
  13. package/dist/plugin/isGlobalHooksSupported.js +10 -0
  14. package/dist/plugin/pluginExport.js +104 -0
  15. package/dist/plugin/server.js +117 -0
  16. package/dist/plugin/webSocket.js +129 -0
  17. package/index.js +3 -2
  18. package/package.json +37 -18
  19. package/src/browser/spec-driver.ts +1 -1
  20. package/src/expose.ts +71 -0
  21. package/src/plugin/{concurrencyMsg.js → concurrencyMsg.ts} +1 -1
  22. package/src/plugin/{config.js → config.ts} +6 -15
  23. package/src/plugin/{configParams.js → configParams.ts} +1 -3
  24. package/src/plugin/{errorDigest.js → errorDigest.ts} +34 -38
  25. package/src/plugin/{getErrorsAndDiffs.js → getErrorsAndDiffs.ts} +3 -8
  26. package/src/plugin/{handleTestResults.js → handleTestResults.ts} +17 -12
  27. package/src/plugin/hooks.ts +60 -0
  28. package/src/plugin/index.ts +37 -0
  29. package/src/plugin/isGlobalHooksSupported.ts +11 -0
  30. package/src/plugin/pluginExport.ts +118 -0
  31. package/src/plugin/{server.js → server.ts} +30 -33
  32. package/src/plugin/{webSocket.js → webSocket.ts} +34 -23
  33. package/src/setup/handlePlugin.js +21 -2
  34. package/src/setup/isPluginDefinedESM.js +5 -0
  35. package/index.d.ts +0 -86
  36. package/src/plugin/defaultPort.js +0 -1
  37. package/src/plugin/hooks.js +0 -41
  38. package/src/plugin/isGlobalHooksSupported.js +0 -13
  39. package/src/plugin/pluginExport.js +0 -94
  40. package/src/plugin/startPlugin.js +0 -15
@@ -1,13 +1,11 @@
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 {formatters} = require('@applitools/core')
8
- const {resolve} = require('path')
1
+ import errorDigest from './errorDigest'
2
+ import {makeLogger} from '@applitools/logger'
3
+ import getErrorsAndDiffs from './getErrorsAndDiffs'
4
+ import fs from 'fs'
5
+ import {formatters} from '@applitools/core'
6
+ import {resolve} from 'path'
9
7
 
10
- function printTestResults(testResultsArr) {
8
+ function printTestResults(testResultsArr: any) {
11
9
  const logger = makeLogger({
12
10
  level: testResultsArr.resultConfig.showLogs ? 'info' : 'silent',
13
11
  label: 'eyes',
@@ -26,10 +24,17 @@ function printTestResults(testResultsArr) {
26
24
  )
27
25
  }
28
26
  }
29
- function handleBatchResultsFile(results, tapFileConfig) {
27
+ function handleBatchResultsFile(results: any, tapFileConfig: any) {
30
28
  const fileName = tapFileConfig.tapFileName || `${new Date().toISOString()}-eyes.tap`
31
29
  const tapFile = resolve(tapFileConfig.tapDirPath, fileName)
32
- return writeFile(tapFile, formatters.toHierarchicTAPString(results, {includeSubTests: false, markNewAsPassed: true}))
30
+ return fs.writeFile(
31
+ tapFile,
32
+ formatters.toHierarchicTAPString(results, {includeSubTests: false, markNewAsPassed: true}),
33
+ {},
34
+ (err: any) => {
35
+ if (err) throw err
36
+ },
37
+ )
33
38
  }
34
39
 
35
- module.exports = {printTestResults, handleBatchResultsFile}
40
+ export default {printTestResults, handleBatchResultsFile}
@@ -0,0 +1,60 @@
1
+ import handleTestResults from './handleTestResults'
2
+ export type EyesCypressAction = 'before:run' | 'after:run'
3
+
4
+ declare global {
5
+ // eslint-disable-next-line @typescript-eslint/no-namespace
6
+ namespace Cypress {
7
+ interface ResolvedConfigOptions {
8
+ appliConfFile: {
9
+ dontCloseBatches: boolean
10
+ batch: any
11
+ serverUrl: string
12
+ proxy: string
13
+ apiKey: string
14
+ batchId: string
15
+ tapDirPath: string
16
+ tapFileName: string
17
+ }
18
+ }
19
+ }
20
+ }
21
+
22
+ export default function makeGlobalRunHooks({closeManager, closeBatches, closeUniversalServer}: any): {
23
+ 'after:run': (results: CypressCommandLine.CypressRunResult) => void | Promise<void>
24
+ 'before:run': (runDetails: Cypress.BeforeRunDetails) => void | Promise<void>
25
+ } {
26
+ return {
27
+ 'before:run': ({config}: Cypress.BeforeRunDetails): void => {
28
+ if (!(config as Cypress.Config).isTextTerminal) return
29
+ },
30
+
31
+ 'after:run': async ({config}: CypressCommandLine.CypressRunResult) => {
32
+ try {
33
+ if (!(config as Cypress.Config).isTextTerminal) return
34
+ const summaries = await closeManager()
35
+
36
+ let testResults
37
+ for (const summary of summaries) {
38
+ testResults = summary.results.map(({testResults}: any) => testResults)
39
+ }
40
+ if (!config.appliConfFile.dontCloseBatches) {
41
+ await closeBatches({
42
+ batchIds: [config.appliConfFile.batchId || config.appliConfFile.batch.id],
43
+ serverUrl: config.appliConfFile.serverUrl,
44
+ proxy: config.appliConfFile.proxy,
45
+ apiKey: config.appliConfFile.apiKey,
46
+ })
47
+ }
48
+
49
+ if (config.appliConfFile.tapDirPath) {
50
+ await handleTestResults.handleBatchResultsFile(testResults, {
51
+ tapDirPath: config.appliConfFile.tapDirPath,
52
+ tapFileName: config.appliConfFile.tapFileName,
53
+ })
54
+ }
55
+ } finally {
56
+ await closeUniversalServer()
57
+ }
58
+ },
59
+ }
60
+ }
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+ import makePluginExport from './pluginExport'
3
+ import makeConfig from './config'
4
+ import makeStartServer from './server'
5
+ import {makeLogger} from '@applitools/logger'
6
+
7
+ // DON'T REMOVE
8
+ //
9
+ // if remove the `ttsc` will compile the absolute path
10
+ //
11
+ // the absolute path is added because api-extractor goes over the `eyesPlugin`
12
+ // declaration before it goes over the `EyesConfig` definition, and this is why
13
+ // it's important to reverse the order
14
+ export type EyesPluginConfig = {
15
+ tapDirPath: string
16
+ tapFileName: string
17
+ eyesIsDisabled: boolean
18
+ eyesBrowser: any
19
+ eyesLayoutBreakpoints: any
20
+ eyesFailCypressOnDiff: boolean
21
+ eyesDisableBrowserFetching: boolean
22
+ eyesTestConcurrency: number
23
+ eyesWaitBeforeCapture: number
24
+ eyesPort?: number
25
+ }
26
+
27
+ const {config, eyesConfig} = makeConfig()
28
+ const logger = makeLogger({level: config.showLogs ? 'info' : 'silent', label: 'eyes'})
29
+
30
+ const startServer = makeStartServer({logger})
31
+
32
+ const pluginExport = makePluginExport({
33
+ startServer,
34
+ eyesConfig: Object.assign({}, eyesConfig, {appliConfFile: config}),
35
+ })
36
+
37
+ export default pluginExport
@@ -0,0 +1,11 @@
1
+ const CYPRESS_SUPPORTED_VERSION = '6.2.0'
2
+ const CYPRESS_NO_FLAG_VERSION = '6.7.0'
3
+
4
+ export default function isGlobalHooksSupported(config: any) {
5
+ const {version, experimentalRunEvents} = config
6
+
7
+ return (
8
+ parseFloat(version) >= parseFloat(CYPRESS_NO_FLAG_VERSION) ||
9
+ (parseFloat(version) >= parseFloat(CYPRESS_SUPPORTED_VERSION) && !!experimentalRunEvents)
10
+ )
11
+ }
@@ -0,0 +1,118 @@
1
+ import isGlobalHooksSupported from './isGlobalHooksSupported'
2
+ // @ts-ignore
3
+ import {presult} from '@applitools/functional-commons'
4
+ import makeGlobalRunHooks from './hooks'
5
+ import {type EyesPluginConfig} from './'
6
+
7
+ export default function makePluginExport({startServer, eyesConfig}: any) {
8
+ return function pluginExport(pluginInitArgs: Cypress.ConfigOptions | NodeJS.Module) {
9
+ let eyesServer: any, pluginModuleExports: any, pluginExportsE2E: any, pluginExportsComponent: any
10
+ let pluginExports
11
+ if ((pluginInitArgs as NodeJS.Module).exports) {
12
+ const pluginAsNodeJSModule = pluginInitArgs as NodeJS.Module
13
+ pluginExports =
14
+ pluginAsNodeJSModule.exports && pluginAsNodeJSModule.exports.default
15
+ ? pluginAsNodeJSModule.exports.default
16
+ : pluginAsNodeJSModule.exports
17
+
18
+ if (pluginExports.component) {
19
+ pluginExportsComponent = pluginExports.component.setupNodeEvents
20
+ }
21
+ if (pluginExports.e2e) {
22
+ pluginExportsE2E = pluginExports.e2e.setupNodeEvents
23
+ }
24
+ if (!pluginExports.e2e && !pluginExports.component) {
25
+ pluginModuleExports = pluginExports
26
+ }
27
+ if (pluginExports?.component) {
28
+ pluginExports.component.setupNodeEvents = setupNodeEvents
29
+ }
30
+ if (pluginExports?.e2e) {
31
+ pluginExports.e2e.setupNodeEvents = setupNodeEvents
32
+ }
33
+ if (!pluginExports.component && !pluginExports.e2e) {
34
+ if (pluginAsNodeJSModule.exports.default) {
35
+ pluginAsNodeJSModule.exports.default = setupNodeEvents
36
+ } else {
37
+ pluginAsNodeJSModule.exports = setupNodeEvents
38
+ }
39
+ }
40
+ } else {
41
+ // this is required because we are currently support cypress < 10
42
+ // in the version before 10 the `e2e.setupNodeEvents` and `component.setupNodeEvents` were not supported
43
+ const pluginAsCypress10PluginOptions = pluginInitArgs as {e2e: {setupNodeEvents: any}; component: {setupNodeEvents: any}}
44
+ if (pluginAsCypress10PluginOptions.component) {
45
+ pluginExportsComponent = pluginAsCypress10PluginOptions.component.setupNodeEvents
46
+ pluginAsCypress10PluginOptions.component.setupNodeEvents = setupNodeEvents
47
+ }
48
+ if (pluginAsCypress10PluginOptions.e2e) {
49
+ pluginExportsE2E = pluginAsCypress10PluginOptions.e2e.setupNodeEvents
50
+ pluginAsCypress10PluginOptions.e2e.setupNodeEvents = setupNodeEvents
51
+ }
52
+ }
53
+
54
+ if (!(pluginInitArgs as NodeJS.Module).exports) {
55
+ return pluginInitArgs
56
+ }
57
+ return function getCloseServer() {
58
+ return eyesServer.close()
59
+ }
60
+
61
+ async function setupNodeEvents(
62
+ origOn: Cypress.PluginEvents,
63
+ cypressConfig: Cypress.PluginConfigOptions,
64
+ ): Promise<EyesPluginConfig> {
65
+ const {server, port, closeManager, closeBatches, closeUniversalServer} = await startServer()
66
+ eyesServer = server
67
+
68
+ const globalHooks: any = makeGlobalRunHooks({
69
+ closeManager,
70
+ closeBatches,
71
+ closeUniversalServer,
72
+ })
73
+
74
+ if (!pluginModuleExports) {
75
+ pluginModuleExports = cypressConfig.testingType === 'e2e' ? pluginExportsE2E : pluginExportsComponent
76
+ }
77
+
78
+ const isGlobalHookCalledFromUserHandlerMap = new Map()
79
+ eyesConfig.eyesIsGlobalHooksSupported = isGlobalHooksSupported(cypressConfig)
80
+ let moduleExportsResult = {}
81
+ // in case setupNodeEvents is not defined in cypress.config file
82
+ if (typeof pluginModuleExports === 'function') {
83
+ moduleExportsResult = await pluginModuleExports(onThatCallsUserDefinedHandler, cypressConfig)
84
+ }
85
+ if (eyesConfig.eyesIsGlobalHooksSupported) {
86
+ for (const [eventName, eventHandler] of Object.entries(globalHooks)) {
87
+ if (!isGlobalHookCalledFromUserHandlerMap.get(eventName)) {
88
+ origOn.call(this, eventName, eventHandler)
89
+ }
90
+ }
91
+ }
92
+
93
+ return Object.assign({}, eyesConfig, {eyesPort: port}, moduleExportsResult)
94
+
95
+ // This piece of code exists because at the point of writing, Cypress does not support multiple event handlers:
96
+ // https://github.com/cypress-io/cypress/issues/5240#issuecomment-948277554
97
+ // So we wrap Cypress' `on` function in order to wrap the user-defined handler. This way we can call our own handler
98
+ // in addition to the user's handler
99
+ function onThatCallsUserDefinedHandler(eventName: string, handler: any) {
100
+ const isRunEvent = eventName === 'before:run' || eventName === 'after:run'
101
+ let handlerToCall = handler
102
+ if (eyesConfig.eyesIsGlobalHooksSupported && isRunEvent) {
103
+ handlerToCall = handlerThatCallsUserDefinedHandler
104
+ isGlobalHookCalledFromUserHandlerMap.set(eventName, true)
105
+ }
106
+ return origOn.call(this, eventName, handlerToCall)
107
+
108
+ async function handlerThatCallsUserDefinedHandler(...args: any[]) {
109
+ const [err] = await presult(Promise.resolve(globalHooks[eventName].apply(this, args)))
110
+ await handler.apply(this, args)
111
+ if (err) {
112
+ throw err
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
@@ -1,38 +1,37 @@
1
- 'use strict'
2
- const connectSocket = require('./webSocket')
3
- const {makeServerProcess} = require('@applitools/eyes-universal')
4
- const handleTestResults = require('./handleTestResults')
5
- const path = require('path')
6
- const fs = require('fs')
7
- const semverLt = require('semver/functions/lt')
8
- const {Server: HttpsServer} = require('https')
9
- const {Server: WSServer} = require('ws')
10
- const which = require('which')
11
-
12
- function makeStartServer({logger}) {
1
+ import connectSocket, {type SocketWithUniversal} from './webSocket'
2
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3
+ // @ts-ignore
4
+ import {makeServerProcess} from '@applitools/eyes-universal'
5
+ import handleTestResults from './handleTestResults'
6
+ import path from 'path'
7
+ import fs from 'fs'
8
+ import {lt as semverLt} from 'semver'
9
+ import {Server as HttpsServer} from 'https'
10
+ import {Server as WSServer} from 'ws'
11
+ import which from 'which'
12
+ import {type Logger} from '@applitools/logger'
13
+ import {AddressInfo} from 'net'
14
+ import {promisify} from 'util'
15
+
16
+ export default function makeStartServer({logger}: {logger: Logger}) {
13
17
  return async function startServer() {
14
- const key = fs.readFileSync(path.resolve(__dirname, '../pem/server.key'))
15
- const cert = fs.readFileSync(path.resolve(__dirname, '../pem/server.cert'))
16
- let port
17
-
18
+ const key = fs.readFileSync(path.resolve(__dirname, '../../src/pem/server.key'))
19
+ const cert = fs.readFileSync(path.resolve(__dirname, '../../src/pem/server.cert'))
18
20
  const https = new HttpsServer({
19
21
  key,
20
22
  cert,
21
23
  })
22
- await https.listen(0, err => {
23
- if (err) {
24
- logger.log('error starting plugin server', err)
25
- } else {
26
- logger.log(`plugin server running at port: ${https.address().port}`)
27
- port = https.address().port
28
- }
29
- })
24
+ await promisify(https.listen.bind(https))()
30
25
 
26
+ const port = (https.address() as AddressInfo).port
31
27
  const wss = new WSServer({server: https, path: '/eyes', maxPayload: 254 * 1024 * 1024})
32
28
 
33
29
  wss.on('close', () => https.close())
34
30
 
35
- const forkOptions = {
31
+ const forkOptions: {
32
+ detached: boolean
33
+ execPath?: string
34
+ } = {
36
35
  detached: true,
37
36
  }
38
37
 
@@ -53,13 +52,13 @@ function makeStartServer({logger}) {
53
52
  portResolutionMode: 'random',
54
53
  })
55
54
 
56
- const managers = []
57
- let socketWithUniversal
55
+ const managers: {manager: object; socketWithUniversal: SocketWithUniversal}[] = []
56
+ let socketWithUniversal: SocketWithUniversal
58
57
 
59
58
  wss.on('connection', socketWithClient => {
60
59
  socketWithUniversal = connectSocket(`ws://localhost:${universalPort}/eyes`)
61
60
 
62
- socketWithUniversal.setPassthroughListener(message => {
61
+ socketWithUniversal.setPassthroughListener((message: string) => {
63
62
  logger.log('<== ', message.toString().slice(0, 1000))
64
63
  const {name, payload} = JSON.parse(message)
65
64
  if (name === 'Core.makeManager') {
@@ -69,7 +68,7 @@ function makeStartServer({logger}) {
69
68
  socketWithClient.send(message.toString())
70
69
  })
71
70
 
72
- socketWithClient.on('message', message => {
71
+ socketWithClient.on('message', (message: string) => {
73
72
  const msg = JSON.parse(message)
74
73
  logger.log('==> ', message.toString().slice(0, 1000))
75
74
  if (msg.name === 'Core.makeSDK') {
@@ -134,13 +133,11 @@ function makeStartServer({logger}) {
134
133
  ),
135
134
  )
136
135
  }
137
- function closeBatches(settings) {
136
+ function closeBatches(settings: any) {
138
137
  if (socketWithUniversal)
139
- return socketWithUniversal.request('Core.closeBatches', {settings}).catch(err => {
138
+ return socketWithUniversal.request('Core.closeBatches', {settings}).catch((err: Error) => {
140
139
  logger.log('@@@', err)
141
140
  })
142
141
  }
143
142
  }
144
143
  }
145
-
146
- module.exports = makeStartServer
@@ -1,26 +1,39 @@
1
- const WebSocket = require('ws')
2
- const {v4: uuid} = require('uuid')
1
+ import WebSocket from 'ws'
2
+ import {v4 as uuid} from 'uuid'
3
+ import {type Socket} from 'net'
4
+
5
+ export type SocketWithUniversal = {
6
+ setPassthroughListener: (_listener: (_message: any) => void) => void
7
+ once(_type: any, _fn: any): any
8
+ on(_type: any, _fn: any): any
9
+ off(_type: any, _fn: any): any
10
+ disconnect(): void
11
+ ref(): () => boolean
12
+ unref(): () => boolean
13
+ send(_message: any): () => boolean
14
+ request(_name: string, _payload: any): any
15
+ }
3
16
 
4
- function connectSocket(url) {
5
- const socket = new WebSocket(url)
6
- let passthroughListener
17
+ export default function connectSocket(url: string): SocketWithUniversal {
18
+ const socket: WebSocket & {_socket?: Socket} = new WebSocket(url)
19
+ let passthroughListener: any
7
20
  const listeners = new Map()
8
- const queue = new Set()
21
+ const queue = new Set<any>()
9
22
  let isReady = false
10
23
 
11
24
  attach()
12
25
 
13
26
  function attach() {
14
- if (socket.readyState === WebSocket.CONNECTING) socket.on('open', () => attach(socket))
27
+ if (socket.readyState === WebSocket.CONNECTING) socket.on('open', () => attach())
15
28
  else if (socket.readyState === WebSocket.OPEN) {
16
29
  isReady = true
17
- queue.forEach(command => command())
30
+ queue.forEach((command: any) => command())
18
31
  queue.clear()
19
32
 
20
33
  socket.on('message', message => {
21
34
  const {name, key, payload} = deserialize(message)
22
- const fns = listeners.get(name)
23
- const keyListeners = key && listeners.get(`${name}/${key}`)
35
+ const fns: any[] = listeners.get(name)
36
+ const keyListeners: any[] = key && listeners.get(`${name}/${key}`)
24
37
  if (fns) fns.forEach(fn => fn(payload, key))
25
38
  if (keyListeners) keyListeners.forEach(fn => fn(payload, key))
26
39
 
@@ -39,18 +52,18 @@ function connectSocket(url) {
39
52
  queue.clear()
40
53
  }
41
54
 
42
- function setPassthroughListener(fn) {
55
+ function setPassthroughListener(fn: any) {
43
56
  passthroughListener = fn
44
57
  }
45
58
 
46
- function send(message) {
59
+ function send(message: string) {
47
60
  const command = () => socket.send(message)
48
61
  if (isReady) command()
49
62
  else queue.add(command)
50
63
  return () => queue.delete(command)
51
64
  }
52
65
 
53
- function on(type, fn) {
66
+ function on(type: string | {name: string; key: string}, fn: any) {
54
67
  const name = typeof type === 'string' ? type : `${type.name}/${type.key}`
55
68
  let fns = listeners.get(name)
56
69
  if (!fns) {
@@ -61,12 +74,12 @@ function connectSocket(url) {
61
74
  return () => off(name, fn)
62
75
  }
63
76
 
64
- function once(type, fn) {
65
- const off = on(type, (...args) => (fn(...args), off()))
77
+ function once(type: any, fn: any) {
78
+ const off = on(type, (...args: any[]) => (fn(...args), off()))
66
79
  return off
67
80
  }
68
81
 
69
- function off(name, fn) {
82
+ function off(name: string, fn: any) {
70
83
  if (!fn) return listeners.delete(name)
71
84
  const fns = listeners.get(name)
72
85
  if (!fns) return false
@@ -75,15 +88,15 @@ function connectSocket(url) {
75
88
  return existed
76
89
  }
77
90
 
78
- function emit(type, payload) {
91
+ function emit(type: any, payload: any) {
79
92
  return send(serialize(type, payload))
80
93
  }
81
94
 
82
- function request(name, payload) {
95
+ function request(name: string, payload: any) {
83
96
  return new Promise((resolve, reject) => {
84
97
  const key = uuid()
85
98
  emit({name, key}, payload)
86
- once({name, key}, response => {
99
+ once({name, key}, (response: any) => {
87
100
  if (response.error) return reject(response.error)
88
101
  return resolve(response.result)
89
102
  })
@@ -117,13 +130,11 @@ function connectSocket(url) {
117
130
  }
118
131
  }
119
132
 
120
- function serialize(type, payload) {
133
+ function serialize(type: any, payload: any) {
121
134
  const message = typeof type === 'string' ? {name: type, payload} : {name: type.name, key: type.key, payload}
122
135
  return JSON.stringify(message)
123
136
  }
124
137
 
125
- function deserialize(message) {
138
+ function deserialize(message: any) {
126
139
  return JSON.parse(message)
127
140
  }
128
-
129
- module.exports = connectSocket
@@ -1,16 +1,35 @@
1
1
  'use strict'
2
2
 
3
3
  const chalk = require('chalk')
4
+ const boxen = require('boxen')
4
5
  const {addEyesCypressPlugin} = require('./addEyesCypressPlugin')
5
6
  const isPluginDefined = require('./isPluginDefined')
7
+ const isPluginDefinedESM = require('./isPluginDefinedESM')
6
8
  const fs = require('fs')
7
9
 
8
10
  function handlePlugin(pluginsFilePath) {
9
11
  const fileContent = fs.readFileSync(pluginsFilePath, 'utf-8')
10
-
11
- if (!isPluginDefined(fileContent)) {
12
+ const isESMOrTS = fileContent.indexOf('module.export') === -1
13
+ if (!isESMOrTS && !isPluginDefined(fileContent)) {
12
14
  fs.writeFileSync(pluginsFilePath, addEyesCypressPlugin(fileContent))
13
15
  console.log(chalk.cyan('Plugins defined.'))
16
+ } else if (isESMOrTS && !isPluginDefinedESM(fileContent)) {
17
+ console.log(
18
+ boxen(
19
+ `
20
+ We detected that you are using TS or ESM syntax. Please configure the plugin as follows:
21
+
22
+ ${chalk.green.bold('import eyesPlugin from "@applitools/eyes-cypress"')}
23
+
24
+ export default ${chalk.green.bold('eyesPlugin(')}definedConfig({
25
+ //...
26
+ })${chalk.green.bold(')')}
27
+
28
+ For more information, visit Eyes-Cypress documentation https://www.npmjs.com/package/@applitools/eyes-cypress (manual configuration section)
29
+ `,
30
+ {padding: 1, borderColor: 'cyan'},
31
+ ),
32
+ )
14
33
  } else {
15
34
  console.log(chalk.cyan('Plugins already defined'))
16
35
  }
@@ -0,0 +1,5 @@
1
+ function isPluginDefinedTypeScript(content) {
2
+ return !!content.match(/from\s*['"]@applitools\/eyes-cypress['"]\s*/)
3
+ }
4
+
5
+ module.exports = isPluginDefinedTypeScript
package/index.d.ts DELETED
@@ -1,86 +0,0 @@
1
- /// <reference types="cypress" />
2
- import type * as api from '@applitools/eyes-api'
3
-
4
- type MaybeArray<T> = T | T[]
5
-
6
- type LegacyRegion = {left: number; top: number; width: number; height: number}
7
- type Selector = {selector: string; type?: 'css' | 'xpath', nodeType?: 'element' | 'shadow-root'} | 'string'
8
- type Element = HTMLElement | JQuery<HTMLElement>
9
- type ElementWithOptions = {element: Element, regionId?: string, padding?: any}
10
-
11
- interface CypressCheckSettings extends api.CheckSettingsAutomationPlain<Element, Selector>{
12
- tag?: CypressCheckSettings['name']
13
-
14
- target?: 'window' | 'region'
15
- selector?: Selector
16
- element?: Element
17
-
18
- ignore?: MaybeArray<NonNullable<CypressCheckSettings['ignoreRegions']>[number] | LegacyRegion | ElementWithOptions>
19
- layout?: MaybeArray<NonNullable<CypressCheckSettings['layoutRegions']>[number] | LegacyRegion| ElementWithOptions>
20
- content?: MaybeArray<NonNullable<CypressCheckSettings['contentRegions']>[number] | LegacyRegion| ElementWithOptions>
21
- strict?: MaybeArray<NonNullable<CypressCheckSettings['strictRegions']>[number] | LegacyRegion | ElementWithOptions>
22
- floating?: MaybeArray<NonNullable<CypressCheckSettings['floatingRegions']>[number] | ((ElementWithOptions | Selector | LegacyRegion) & {maxUpOffset?: number; maxDownOffset?: number; maxLeftOffset?: number; maxRightOffset?: number})>
23
- accessibility?: MaybeArray<NonNullable<CypressCheckSettings['accessibilityRegions']>[number] | ((ElementWithOptions | Selector | LegacyRegion) & {accessibilityType?: api.AccessibilityRegionTypePlain})>
24
- scriptHooks?: CypressCheckSettings['hooks']
25
- ignoreCaret?: boolean
26
- ignoreDisplacements?: boolean
27
- }
28
-
29
- interface CypressEyesConfig extends api.ConfigurationPlain<Element, Selector> {
30
- browser?: MaybeArray<NonNullable<CypressEyesConfig['browsersInfo']>[number] | {deviceName: string; screenOrientation?: api.ScreenOrientationPlain; name?: string}>
31
-
32
- batchId?: NonNullable<CypressEyesConfig['batch']>['id']
33
- batchName?: NonNullable<CypressEyesConfig['batch']>['name']
34
- batchSequence?: NonNullable<CypressEyesConfig['batch']>['sequenceName']
35
- notifyOnCompletion?: NonNullable<CypressEyesConfig['batch']>['notifyOnCompletion']
36
-
37
- envName?: CypressEyesConfig['environmentName']
38
-
39
- accessibilitySettings?: NonNullable<CypressEyesConfig['defaultMatchSettings']>['accessibilitySettings']
40
- }
41
-
42
- declare global {
43
- namespace Cypress {
44
- interface Chainable {
45
- /**
46
- * Create an Applitools test.
47
- * This will start a session with the Applitools server.
48
- * @example
49
- * cy.eyesOpen({ appName: 'My App' })
50
- */
51
- eyesOpen(config?: CypressEyesConfig): null
52
-
53
- /**
54
- * Generate a screenshot of the current page and add it to the Applitools Test.
55
- * @example
56
- * cy.eyesCheckWindow()
57
- *
58
- * OR
59
- *
60
- * cy.eyesCheckWindow({
61
- * target: 'region',
62
- * selector: '.my-element'
63
- * });
64
- */
65
- eyesCheckWindow(tag?: string): null
66
- eyesCheckWindow(settings?: CypressCheckSettings): null
67
-
68
- /**
69
- * Close the applitools test and check that all screenshots are valid.
70
- * @example cy.eyesClose()
71
- */
72
- eyesClose(): null
73
-
74
- /**
75
- * Returns an object with the applitools test results from a given test / test file. This should be called after close.
76
- * @example
77
- * after(() => {
78
- * cy.eyesGetAllTestResults().then(summary => {
79
- * console.log(summary)
80
- * })
81
- * })
82
- */
83
- eyesGetAllTestResults(): Chainable<api.TestResultsSummary>
84
- }
85
- }
86
- }
@@ -1 +0,0 @@
1
- module.exports = 7373
@@ -1,41 +0,0 @@
1
- 'use strict'
2
- const handleTestResults = require('./handleTestResults')
3
-
4
- function makeGlobalRunHooks({closeManager, closeBatches, closeUniversalServer}) {
5
- return {
6
- 'before:run': ({config}) => {
7
- if (!config.isTextTerminal) return
8
- },
9
-
10
- 'after:run': async ({config}) => {
11
- try {
12
- if (!config.isTextTerminal) return
13
- const summaries = await closeManager()
14
-
15
- let testResults
16
- for (const summary of summaries) {
17
- testResults = summary.results.map(({testResults}) => testResults)
18
- }
19
- if (!config.appliConfFile.dontCloseBatches) {
20
- await closeBatches({
21
- batchIds: [config.appliConfFile.batchId || config.appliConfFile.batch.id],
22
- serverUrl: config.appliConfFile.serverUrl,
23
- proxy: config.appliConfFile.proxy,
24
- apiKey: config.appliConfFile.apiKey,
25
- })
26
- }
27
-
28
- if (config.appliConfFile.tapDirPath) {
29
- await handleTestResults.handleBatchResultsFile(testResults, {
30
- tapDirPath: config.appliConfFile.tapDirPath,
31
- tapFileName: config.appliConfFile.tapFileName,
32
- })
33
- }
34
- } finally {
35
- await closeUniversalServer()
36
- }
37
- },
38
- }
39
- }
40
-
41
- module.exports = makeGlobalRunHooks