-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
config_env.spec.ts
266 lines (216 loc) · 10.3 KB
/
config_env.spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
['config', 'env'].forEach((fnName) => {
describe(`cy.origin- Cypress.${fnName}()`, () => {
const USED_KEYS = {
foo: 'cy-origin-foo',
bar: 'cy-origin-bar',
baz: 'cy-origin-baz',
qux: 'cy-origin-qux',
quux: 'cy-origin-quux',
unserializable: 'cy-origin-unserializable',
error: 'cy-origin-error',
}
afterEach(() => {
// @ts-ignore
window.top.__cySkipValidateConfig = true
})
if (fnName === 'config') {
it(`throws if mutating read-only config values with Cypress.config()`, () => {
return new Promise<void>((resolve) => {
// @ts-ignore
window.top.__cySkipValidateConfig = false
cy.on('fail', (err) => {
expect(err.message).to.include('`Cypress.config()` cannot mutate option `chromeWebSecurity` because it is a read-only property.')
resolve()
})
cy.origin('http://foobar.com:3500', () => {
// @ts-ignore
Cypress.config('chromeWebSecurity', false)
})
})
})
}
context('serializable', () => {
it(`syncs initial Cypress.${fnName}() from the primary origin to the secondary (synchronously)`, () => {
Cypress[fnName](USED_KEYS.foo, 'bar')
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const bar = Cypress[fnName](USED_KEYS.foo)
expect(bar).to.equal('bar')
})
})
it(`syncs serializable values in the Cypress.${fnName}() again to the secondary even after spec bridge is created`, () => {
Cypress[fnName](USED_KEYS.foo, 'baz')
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const baz = Cypress[fnName](USED_KEYS.foo)
expect(baz).to.equal('baz')
})
})
it(`syncs serializable Cypress.${fnName}() values outwards from secondary (synchronously)`, () => {
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
Cypress[fnName](USED_KEYS.bar, 'baz')
}).then(() => {
const baz = Cypress[fnName](USED_KEYS.bar)
expect(baz).to.equal('baz')
})
})
it(`syncs serializable Cypress.${fnName}() values outwards from secondary even if the value is undefined`, () => {
Cypress[fnName](USED_KEYS.foo, 'bar')
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
Cypress[fnName](USED_KEYS.foo, undefined)
}).then(() => {
expect(Cypress[fnName]('foo')).to.be.undefined
})
})
it(`syncs serializable Cypress.${fnName}() values outwards from secondary (commands/async)`, () => {
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
cy.then(() => {
Cypress[fnName](USED_KEYS.bar, 'quux')
})
}).then(() => {
const quux = Cypress[fnName](USED_KEYS.bar)
expect(quux).to.equal('quux')
})
})
it(`persists Cypress.${fnName}() changes made in the secondary, assuming primary has not overwritten with a serializable value`, () => {
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const quux = Cypress[fnName](USED_KEYS.bar)
expect(quux).to.equal('quux')
})
})
it(`does NOT sync Cypress.${fnName}() changes made in the secondary after the command queue is finished and the callback window is closed`, {
[USED_KEYS.baz]: undefined,
env: {
[USED_KEYS.baz]: undefined,
},
}, () => {
return new Promise<void>((resolve) => {
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
setTimeout(() => {
// this value STILL gets set, but will be blown away on the next origin with whatever exists in the primary
Cypress[fnName](USED_KEYS.baz, 'qux')
}, 100)
Cypress[fnName](USED_KEYS.baz, 'quux')
}).then(() => {
const quux = Cypress[fnName](USED_KEYS.baz)
expect(quux).to.equal('quux')
resolve()
})
})
})
it('overwrites different values in secondary if one exists in the primary', {
[USED_KEYS.baz]: 'quux',
env: {
[USED_KEYS.baz]: 'quux',
},
}, () => {
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
// in previous test, 'baz' was set to 'qux' after the callback window was closed.
// this should be overwritten by 'quux' that exists in the primary
const quux = Cypress[fnName](USED_KEYS.baz)
expect(quux).to.equal('quux')
})
})
it(`overwrites different values in secondary, even if the Cypress.${fnName}() value does not exist in the primary`, {
[USED_KEYS.baz]: undefined,
env: {
[USED_KEYS.baz]: undefined,
},
}, () => {
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const isNowUndefined = Cypress[fnName](USED_KEYS.baz)
expect(isNowUndefined).to.be.undefined
})
})
})
context('unserializable', () => {
const unserializableFunc = () => undefined
it('does not sync unserializable values from the primary to the secondary', () => {
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const isUndefined = Cypress[fnName](USED_KEYS.unserializable)
expect(isUndefined).to.be.undefined
})
})
it('does not overwrite unserializable values in the primary when none exist in the secondary', () => {
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
cy.origin('http://foobar.com:3500', () => undefined)
const isFunc = Cypress[fnName](USED_KEYS.unserializable)
expect(isFunc).to.equal(unserializableFunc)
})
it('overwrites unserializable values in the primary when serializable values of same key exist in secondary', () => {
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
Cypress[fnName](USED_KEYS.unserializable, undefined)
}).then(() => {
const isNowUndefined = Cypress[fnName](USED_KEYS.unserializable)
expect(isNowUndefined).to.be.undefined
})
})
it('overwrites unserializable values in the secondary when serializable values of same key exist in primary', () => {
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const unserializableFuncSecondary = () => undefined
Cypress[fnName](USED_KEYS.unserializable, unserializableFuncSecondary)
}).then(() => {
Cypress[fnName](USED_KEYS.unserializable, undefined)
})
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const isUndefined = Cypress[fnName](USED_KEYS.unserializable)
expect(isUndefined).to.be.undefined
}).then(() => {
const isUndefined = Cypress[fnName](USED_KEYS.unserializable)
expect(isUndefined).to.be.undefined
})
})
it('does not overwrite unserializable values in the primary when unserializable values of same key exist in secondary', () => {
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const unserializableFuncSecondary = () => undefined
Cypress[fnName](USED_KEYS.unserializable, unserializableFuncSecondary)
}).then(() => {
const isFunc = Cypress[fnName](USED_KEYS.unserializable)
expect(isFunc).to.equal(unserializableFunc)
})
})
it('does not try to merge objects that contain unserializable values', () => {
const partiallyUnserializableObject = {
a: 1,
b: document.createElement('h1'),
}
Cypress[fnName](USED_KEYS.unserializable, partiallyUnserializableObject)
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const doesNotContainPartialAProperty = Cypress[fnName](USED_KEYS.unserializable)
expect(doesNotContainPartialAProperty?.a).to.be.undefined
Cypress[fnName](USED_KEYS.unserializable, {
a: 3,
c: document.createElement('h1'),
})
}).then(() => {
const isPartiallyUnserializableObject = Cypress[fnName](USED_KEYS.unserializable)
expect(isPartiallyUnserializableObject.a).to.equal(1)
})
})
})
context('structuredClone()', () => {
it('(firefox) uses native structuredClone in firefox and does NOT serialize Error objects in config', {
browser: 'firefox',
}, function () {
Cypress[fnName](USED_KEYS.error, new Error('error'))
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const isUndefined = Cypress[fnName](USED_KEYS.error)
expect(isUndefined).to.be.undefined
})
})
// NOTE: chrome 98 and above uses a native structuredClone() method, but that method CAN clone Error objects
it('(chromium) uses ponyfilled or native structuredClone that can serialize Error objects in config', {
browser: { family: 'chromium' },
}, () => {
Cypress[fnName](USED_KEYS.error, new Error('error'))
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
const isErrorObj = Cypress[fnName](USED_KEYS.error)
// We preserve the error structure, but on preprocessing to the spec bridge, the error is converted to a flat object
expect(isErrorObj).to.be.an.instanceof(Object)
expect(isErrorObj.message).to.eq('error')
})
})
})
})
})