cypress 9.0.0 → 9.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 {
|