cypress 9.0.0 → 9.2.1
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.
- package/lib/tasks/download.js +67 -40
- package/lib/tasks/install.js +5 -1
- package/lib/tasks/verify.js +1 -1
- package/package.json +4 -4
- package/types/cypress.d.ts +58 -17
package/lib/tasks/download.js
CHANGED
@@ -8,7 +8,7 @@ const is = require('check-more-types');
|
|
8
8
|
|
9
9
|
const os = require('os');
|
10
10
|
|
11
|
-
const
|
11
|
+
const Url = require('url');
|
12
12
|
|
13
13
|
const path = require('path');
|
14
14
|
|
@@ -36,6 +36,7 @@ const fs = require('../fs');
|
|
36
36
|
const util = require('../util');
|
37
37
|
|
38
38
|
const defaultBaseUrl = 'https://download.cypress.io/';
|
39
|
+
const defaultMaxRedirects = 10;
|
39
40
|
|
40
41
|
const getProxyForUrlWithNpmConfig = url => {
|
41
42
|
return getProxyForUrl(url) || process.env.npm_config_https_proxy || process.env.npm_config_proxy || null;
|
@@ -76,7 +77,7 @@ const getCA = () => {
|
|
76
77
|
};
|
77
78
|
|
78
79
|
const prepend = urlPath => {
|
79
|
-
const endpoint =
|
80
|
+
const endpoint = Url.resolve(getBaseUrl(), urlPath);
|
80
81
|
const platform = os.platform();
|
81
82
|
return `${endpoint}?platform=${platform}&arch=${arch()}`;
|
82
83
|
};
|
@@ -183,8 +184,18 @@ const downloadFromUrl = ({
|
|
183
184
|
url,
|
184
185
|
downloadDestination,
|
185
186
|
progress,
|
186
|
-
ca
|
187
|
+
ca,
|
188
|
+
version,
|
189
|
+
redirectTTL = defaultMaxRedirects
|
187
190
|
}) => {
|
191
|
+
if (redirectTTL <= 0) {
|
192
|
+
return Promise.reject(new Error(stripIndent`
|
193
|
+
Failed downloading the Cypress binary.
|
194
|
+
There were too many redirects. The default allowance is ${defaultMaxRedirects}.
|
195
|
+
Maybe you got stuck in a redirect loop?
|
196
|
+
`));
|
197
|
+
}
|
198
|
+
|
188
199
|
return new Promise((resolve, reject) => {
|
189
200
|
const proxy = getProxyForUrlWithNpmConfig(url);
|
190
201
|
debug('Downloading package', {
|
@@ -192,35 +203,24 @@ const downloadFromUrl = ({
|
|
192
203
|
proxy,
|
193
204
|
downloadDestination
|
194
205
|
});
|
195
|
-
let redirectVersion;
|
196
|
-
const reqOptions = {
|
197
|
-
url,
|
198
|
-
proxy,
|
199
|
-
|
200
|
-
followRedirect(response) {
|
201
|
-
const version = response.headers['x-version'];
|
202
|
-
debug('redirect version:', version);
|
203
|
-
|
204
|
-
if (version) {
|
205
|
-
// set the version in options if we have one.
|
206
|
-
// this insulates us from potential redirect
|
207
|
-
// problems where version would be set to undefined.
|
208
|
-
redirectVersion = version;
|
209
|
-
} // yes redirect
|
210
|
-
|
211
|
-
|
212
|
-
return true;
|
213
|
-
}
|
214
|
-
|
215
|
-
};
|
216
206
|
|
217
207
|
if (ca) {
|
218
208
|
debug('using custom CA details from npm config');
|
219
|
-
reqOptions.agentOptions = {
|
220
|
-
ca
|
221
|
-
};
|
222
209
|
}
|
223
210
|
|
211
|
+
const reqOptions = {
|
212
|
+
uri: url,
|
213
|
+
...(proxy ? {
|
214
|
+
proxy
|
215
|
+
} : {}),
|
216
|
+
...(ca ? {
|
217
|
+
agentOptions: {
|
218
|
+
ca
|
219
|
+
}
|
220
|
+
} : {}),
|
221
|
+
method: 'GET',
|
222
|
+
followRedirect: false
|
223
|
+
};
|
224
224
|
const req = request(reqOptions); // closure
|
225
225
|
|
226
226
|
let started = null;
|
@@ -248,18 +248,46 @@ const downloadFromUrl = ({
|
|
248
248
|
// response headers
|
249
249
|
|
250
250
|
|
251
|
-
started = new Date();
|
252
|
-
|
253
|
-
if (
|
251
|
+
started = new Date();
|
252
|
+
|
253
|
+
if (/^3/.test(response.statusCode)) {
|
254
|
+
const redirectVersion = response.headers['x-version'];
|
255
|
+
const redirectUrl = response.headers.location;
|
256
|
+
debug('redirect version:', redirectVersion);
|
257
|
+
debug('redirect url:', redirectUrl);
|
258
|
+
downloadFromUrl({
|
259
|
+
url: redirectUrl,
|
260
|
+
progress,
|
261
|
+
ca,
|
262
|
+
downloadDestination,
|
263
|
+
version: redirectVersion,
|
264
|
+
redirectTTL: redirectTTL - 1
|
265
|
+
}).then(resolve).catch(reject); // if our status code does not start with 200
|
266
|
+
} else if (!/^2/.test(response.statusCode)) {
|
254
267
|
debug('response code %d', response.statusCode);
|
255
268
|
const err = new Error(stripIndent`
|
256
269
|
Failed downloading the Cypress binary.
|
257
270
|
Response code: ${response.statusCode}
|
258
271
|
Response message: ${response.statusMessage}
|
259
272
|
`);
|
260
|
-
reject(err);
|
273
|
+
reject(err); // status codes here are all 2xx
|
274
|
+
} else {
|
275
|
+
// We only enable this pipe connection when we know we've got a successful return
|
276
|
+
// and handle the completion with verify and resolve
|
277
|
+
// there was a possible race condition between end of request and close of writeStream
|
278
|
+
// that is made ordered with this Promise.all
|
279
|
+
Promise.all([new Promise(r => {
|
280
|
+
return response.pipe(fs.createWriteStream(downloadDestination).on('close', r));
|
281
|
+
}), new Promise(r => response.on('end', r))]).then(() => {
|
282
|
+
debug('downloading finished');
|
283
|
+
verifyDownloadedFile(downloadDestination, expectedSize, expectedChecksum).then(() => debug('verified')).then(() => resolve(version)).catch(reject);
|
284
|
+
});
|
261
285
|
}
|
262
|
-
}).on('error',
|
286
|
+
}).on('error', e => {
|
287
|
+
if (e.code === 'ECONNRESET') return; // sometimes proxies give ECONNRESET but we don't care
|
288
|
+
|
289
|
+
reject(e);
|
290
|
+
}).on('progress', state => {
|
263
291
|
// total time we've elapsed
|
264
292
|
// starting on our first progress notification
|
265
293
|
const elapsed = new Date() - started; // request-progress sends a value between 0 and 1
|
@@ -268,12 +296,6 @@ const downloadFromUrl = ({
|
|
268
296
|
const eta = util.calculateEta(percentage, elapsed); // send up our percent and seconds remaining
|
269
297
|
|
270
298
|
progress.onProgress(percentage, util.secsRemaining(eta));
|
271
|
-
}) // save this download here
|
272
|
-
.pipe(fs.createWriteStream(downloadDestination)).on('finish', () => {
|
273
|
-
debug('downloading finished');
|
274
|
-
verifyDownloadedFile(downloadDestination, expectedSize, expectedChecksum).then(() => {
|
275
|
-
return resolve(redirectVersion);
|
276
|
-
}, reject);
|
277
299
|
});
|
278
300
|
});
|
279
301
|
};
|
@@ -288,7 +310,8 @@ const start = opts => {
|
|
288
310
|
let {
|
289
311
|
version,
|
290
312
|
downloadDestination,
|
291
|
-
progress
|
313
|
+
progress,
|
314
|
+
redirectTTL
|
292
315
|
} = opts;
|
293
316
|
|
294
317
|
if (!downloadDestination) {
|
@@ -316,7 +339,11 @@ const start = opts => {
|
|
316
339
|
url,
|
317
340
|
downloadDestination,
|
318
341
|
progress,
|
319
|
-
ca
|
342
|
+
ca,
|
343
|
+
version,
|
344
|
+
...(redirectTTL ? {
|
345
|
+
redirectTTL
|
346
|
+
} : {})
|
320
347
|
});
|
321
348
|
}).catch(err => {
|
322
349
|
return prettyDownloadErr(err, version);
|
package/lib/tasks/install.js
CHANGED
@@ -64,6 +64,10 @@ const getNpmArgv = () => {
|
|
64
64
|
const getVersionSpecifier = (startDir = path.resolve(__dirname, '../..')) => {
|
65
65
|
const argv = getNpmArgv();
|
66
66
|
|
67
|
+
if ((process.env.npm_package_resolved || '').endsWith('cypress.tgz')) {
|
68
|
+
return process.env.npm_package_resolved;
|
69
|
+
}
|
70
|
+
|
67
71
|
if (argv) {
|
68
72
|
const tgz = _.find(argv, t => t.endsWith('cypress.tgz'));
|
69
73
|
|
@@ -104,7 +108,7 @@ const getVersionSpecifier = (startDir = path.resolve(__dirname, '../..')) => {
|
|
104
108
|
});
|
105
109
|
};
|
106
110
|
|
107
|
-
const betaNpmUrlRe = /^\/beta\/npm\/(?<version>[0-9.]+)\/(?<artifactSlug
|
111
|
+
const betaNpmUrlRe = /^\/beta\/npm\/(?<version>[0-9.]+)\/(?<artifactSlug>.+?)\/cypress\.tgz$/; // convert a prerelease NPM package .tgz URL to the corresponding binary .zip URL
|
108
112
|
|
109
113
|
const getBinaryUrlFromPrereleaseNpmUrl = npmUrl => {
|
110
114
|
let parsed;
|
package/lib/tasks/verify.js
CHANGED
@@ -37,7 +37,7 @@ const xvfb = require('../exec/xvfb');
|
|
37
37
|
|
38
38
|
const state = require('./state');
|
39
39
|
|
40
|
-
const VERIFY_TEST_RUNNER_TIMEOUT_MS = 30000;
|
40
|
+
const VERIFY_TEST_RUNNER_TIMEOUT_MS = +process.env.CYPRESS_VERIFY_TIMEOUT || 30000;
|
41
41
|
|
42
42
|
const checkExecutable = binaryDir => {
|
43
43
|
const executable = state.getPathToExecutable(binaryDir);
|
package/package.json
CHANGED
@@ -1,25 +1,25 @@
|
|
1
1
|
{
|
2
2
|
"name": "cypress",
|
3
|
-
"version": "9.
|
3
|
+
"version": "9.2.1",
|
4
4
|
"main": "index.js",
|
5
5
|
"scripts": {
|
6
6
|
"postinstall": "node index.js --exec install",
|
7
7
|
"size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";"
|
8
8
|
},
|
9
9
|
"dependencies": {
|
10
|
-
"@cypress/request": "^2.88.
|
10
|
+
"@cypress/request": "^2.88.10",
|
11
11
|
"@cypress/xvfb": "^1.2.4",
|
12
12
|
"@types/node": "^14.14.31",
|
13
13
|
"@types/sinonjs__fake-timers": "^6.0.2",
|
14
14
|
"@types/sizzle": "^2.3.2",
|
15
15
|
"arch": "^2.2.0",
|
16
16
|
"blob-util": "^2.0.2",
|
17
|
-
"bluebird": "
|
17
|
+
"bluebird": "3.7.2",
|
18
18
|
"cachedir": "^2.3.0",
|
19
19
|
"chalk": "^4.1.0",
|
20
20
|
"check-more-types": "^2.24.0",
|
21
21
|
"cli-cursor": "^3.1.0",
|
22
|
-
"cli-table3": "~0.6.
|
22
|
+
"cli-table3": "~0.6.1",
|
23
23
|
"commander": "^5.1.0",
|
24
24
|
"common-tags": "^1.8.0",
|
25
25
|
"dayjs": "^1.10.4",
|
package/types/cypress.d.ts
CHANGED
@@ -7,13 +7,38 @@ declare namespace Cypress {
|
|
7
7
|
type HttpMethod = string
|
8
8
|
type RequestBody = string | object
|
9
9
|
type ViewportOrientation = 'portrait' | 'landscape'
|
10
|
-
type PrevSubject =
|
10
|
+
type PrevSubject = keyof PrevSubjectMap
|
11
11
|
type TestingType = 'e2e' | 'component'
|
12
12
|
type PluginConfig = (on: PluginEvents, config: PluginConfigOptions) => void | ConfigOptions | Promise<ConfigOptions>
|
13
13
|
|
14
|
+
interface PrevSubjectMap<O = unknown> {
|
15
|
+
optional: O
|
16
|
+
element: JQuery
|
17
|
+
document: Document
|
18
|
+
window: Window
|
19
|
+
}
|
20
|
+
|
14
21
|
interface CommandOptions {
|
15
22
|
prevSubject: boolean | PrevSubject | PrevSubject[]
|
16
23
|
}
|
24
|
+
interface CommandFn<T extends keyof ChainableMethods> {
|
25
|
+
(this: Mocha.Context, ...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]> | void
|
26
|
+
}
|
27
|
+
interface CommandFnWithSubject<T extends keyof ChainableMethods, S> {
|
28
|
+
(this: Mocha.Context, prevSubject: S, ...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]> | void
|
29
|
+
}
|
30
|
+
interface CommandOriginalFn<T extends keyof ChainableMethods> extends CallableFunction {
|
31
|
+
(...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]>
|
32
|
+
}
|
33
|
+
interface CommandOriginalFnWithSubject<T extends keyof ChainableMethods, S> extends CallableFunction {
|
34
|
+
(prevSubject: S, ...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]>
|
35
|
+
}
|
36
|
+
interface CommandFnWithOriginalFn<T extends keyof Chainable> {
|
37
|
+
(this: Mocha.Context, originalFn: CommandOriginalFn<T>, ...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]> | void
|
38
|
+
}
|
39
|
+
interface CommandFnWithOriginalFnAndSubject<T extends keyof Chainable, S> {
|
40
|
+
(this: Mocha.Context, originalFn: CommandOriginalFnWithSubject<T, S>, prevSubject: S, ...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]> | void
|
41
|
+
}
|
17
42
|
interface ObjectLike {
|
18
43
|
[key: string]: any
|
19
44
|
}
|
@@ -294,6 +319,11 @@ declare namespace Cypress {
|
|
294
319
|
*/
|
295
320
|
LocalStorage: LocalStorage
|
296
321
|
|
322
|
+
/**
|
323
|
+
* Internal class for session management.
|
324
|
+
*/
|
325
|
+
session: Session
|
326
|
+
|
297
327
|
/**
|
298
328
|
* Current testing type, determined by the Test Runner chosen to run.
|
299
329
|
*/
|
@@ -328,7 +358,7 @@ declare namespace Cypress {
|
|
328
358
|
// 60000
|
329
359
|
```
|
330
360
|
*/
|
331
|
-
config<K extends keyof
|
361
|
+
config<K extends keyof Config>(key: K): Config[K]
|
332
362
|
/**
|
333
363
|
* Sets one configuration value.
|
334
364
|
* @see https://on.cypress.io/config
|
@@ -337,7 +367,7 @@ declare namespace Cypress {
|
|
337
367
|
Cypress.config('viewportWidth', 800)
|
338
368
|
```
|
339
369
|
*/
|
340
|
-
config<K extends keyof
|
370
|
+
config<K extends keyof TestConfigOverrides>(key: K, value: TestConfigOverrides[K]): void
|
341
371
|
/**
|
342
372
|
* Sets multiple configuration values at once.
|
343
373
|
* @see https://on.cypress.io/config
|
@@ -420,9 +450,16 @@ declare namespace Cypress {
|
|
420
450
|
* @see https://on.cypress.io/api/commands
|
421
451
|
*/
|
422
452
|
Commands: {
|
423
|
-
add<T extends keyof Chainable>(name: T, fn:
|
424
|
-
add<T extends keyof Chainable>(name: T, options: CommandOptions, fn:
|
425
|
-
|
453
|
+
add<T extends keyof Chainable>(name: T, fn: CommandFn<T>): void
|
454
|
+
add<T extends keyof Chainable>(name: T, options: CommandOptions & {prevSubject: false}, fn: CommandFn<T>): void
|
455
|
+
add<T extends keyof Chainable, S extends PrevSubject>(
|
456
|
+
name: T, options: CommandOptions & { prevSubject: true | S | ['optional'] }, fn: CommandFnWithSubject<T, PrevSubjectMap[S]>,
|
457
|
+
): void
|
458
|
+
add<T extends keyof Chainable, S extends PrevSubject>(
|
459
|
+
name: T, options: CommandOptions & { prevSubject: S[] }, fn: CommandFnWithSubject<T, PrevSubjectMap<void>[S]>,
|
460
|
+
): void
|
461
|
+
overwrite<T extends keyof Chainable>(name: T, fn: CommandFnWithOriginalFn<T>): void
|
462
|
+
overwrite<T extends keyof Chainable, S extends PrevSubject>(name: T, fn: CommandFnWithOriginalFnAndSubject<T, PrevSubjectMap[S]>): void
|
426
463
|
}
|
427
464
|
|
428
465
|
/**
|
@@ -2209,12 +2246,9 @@ declare namespace Cypress {
|
|
2209
2246
|
* @see https://on.cypress.io/writefile
|
2210
2247
|
```
|
2211
2248
|
cy.writeFile('path/to/message.txt', 'Hello World')
|
2212
|
-
.then((text) => {
|
2213
|
-
expect(text).to.equal('Hello World') // true
|
2214
|
-
})
|
2215
2249
|
```
|
2216
2250
|
*/
|
2217
|
-
writeFile
|
2251
|
+
writeFile(filePath: string, contents: FileContents, encoding: Encodings): Chainable<null>
|
2218
2252
|
/**
|
2219
2253
|
* Write to a file with the specified encoding and contents.
|
2220
2254
|
*
|
@@ -2223,12 +2257,10 @@ declare namespace Cypress {
|
|
2223
2257
|
cy.writeFile('path/to/ascii.txt', 'Hello World', {
|
2224
2258
|
flag: 'a+',
|
2225
2259
|
encoding: 'ascii'
|
2226
|
-
}).then((text) => {
|
2227
|
-
expect(text).to.equal('Hello World') // true
|
2228
2260
|
})
|
2229
2261
|
```
|
2230
2262
|
*/
|
2231
|
-
writeFile
|
2263
|
+
writeFile(filePath: string, contents: FileContents, options?: Partial<WriteFileOptions & Timeoutable>): Chainable<null>
|
2232
2264
|
/**
|
2233
2265
|
* Write to a file with the specified encoding and contents.
|
2234
2266
|
*
|
@@ -2238,12 +2270,10 @@ declare namespace Cypress {
|
|
2238
2270
|
```
|
2239
2271
|
cy.writeFile('path/to/ascii.txt', 'Hello World', 'utf8', {
|
2240
2272
|
flag: 'a+',
|
2241
|
-
}).then((text) => {
|
2242
|
-
expect(text).to.equal('Hello World') // true
|
2243
2273
|
})
|
2244
2274
|
```
|
2245
2275
|
*/
|
2246
|
-
writeFile
|
2276
|
+
writeFile(filePath: string, contents: FileContents, encoding: Encodings, options?: Partial<WriteFileOptions & Timeoutable>): Chainable<null>
|
2247
2277
|
|
2248
2278
|
/**
|
2249
2279
|
* jQuery library bound to the AUT
|
@@ -2255,6 +2285,12 @@ declare namespace Cypress {
|
|
2255
2285
|
$$<TElement extends Element = HTMLElement>(selector: JQuery.Selector, context?: Element | Document | JQuery): JQuery<TElement>
|
2256
2286
|
}
|
2257
2287
|
|
2288
|
+
type ChainableMethods<Subject = any> = {
|
2289
|
+
[P in keyof Chainable<Subject>]: Chainable<Subject>[P] extends ((...args: any[]) => any)
|
2290
|
+
? Chainable<Subject>[P]
|
2291
|
+
: never
|
2292
|
+
}
|
2293
|
+
|
2258
2294
|
interface SinonSpyAgent<A extends sinon.SinonSpy> {
|
2259
2295
|
log(shouldOutput?: boolean): Omit<A, 'withArgs'> & Agent<A>
|
2260
2296
|
|
@@ -2886,7 +2922,7 @@ declare namespace Cypress {
|
|
2886
2922
|
xhrUrl: string
|
2887
2923
|
}
|
2888
2924
|
|
2889
|
-
interface TestConfigOverrides extends Partial<Pick<ConfigOptions, 'animationDistanceThreshold' | 'baseUrl' | 'defaultCommandTimeout' | 'env' | 'execTimeout' | 'includeShadowDom' | 'requestTimeout' | 'responseTimeout' | 'retries' | 'scrollBehavior' | 'taskTimeout' | 'viewportHeight' | 'viewportWidth' | 'waitForAnimations'>> {
|
2925
|
+
interface TestConfigOverrides extends Partial<Pick<ConfigOptions, 'animationDistanceThreshold' | 'baseUrl' | 'blockHosts' | 'defaultCommandTimeout' | 'env' | 'execTimeout' | 'includeShadowDom' | 'numTestsKeptInMemory' | 'pageLoadTimeout' | 'redirectionLimit' | 'requestTimeout' | 'responseTimeout' | 'retries' | 'screenshotOnRunFailure' | 'slowTestThreshold' | 'scrollBehavior' | 'taskTimeout' | 'viewportHeight' | 'viewportWidth' | 'waitForAnimations'>> {
|
2890
2926
|
browser?: IsBrowserMatcher | IsBrowserMatcher[]
|
2891
2927
|
keystrokeDelay?: number
|
2892
2928
|
}
|
@@ -3086,6 +3122,11 @@ declare namespace Cypress {
|
|
3086
3122
|
onAnyAbort(route: RouteOptions, proxy: any): void
|
3087
3123
|
}
|
3088
3124
|
|
3125
|
+
interface Session {
|
3126
|
+
// Clear all saved sessions and re-run the current spec file.
|
3127
|
+
clearAllSavedSessions: () => Promise<void>
|
3128
|
+
}
|
3129
|
+
|
3089
3130
|
type SameSiteStatus = 'no_restriction' | 'strict' | 'lax'
|
3090
3131
|
|
3091
3132
|
interface SetCookieOptions extends Loggable, Timeoutable {
|