-
Notifications
You must be signed in to change notification settings - Fork 5.2k
/
Copy pathcommands.ts
168 lines (153 loc) · 6.05 KB
/
commands.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
import 'cypress-hardhat/lib/browser'
import { Eip1193 } from 'cypress-hardhat/lib/browser/eip1193'
import { FeatureFlagClient, FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/gating/flags'
import { ALLOW_ANALYTICS_ATOM_KEY } from 'utilities/src/telemetry/analytics/constants'
import { UserState, initialState } from '../../src/state/user/reducer'
import { setInitialUserState } from '../utils/user-state'
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface ApplicationWindow {
ethereum: Eip1193
}
interface Chainable<Subject> {
/**
* Wait for a specific event to be sent to amplitude. If the event is found, the subject will be the event.
*
* @param {string} eventName - The type of the event to search for e.g. SwapEventName.SWAP_TRANSACTION_COMPLETED
* @param {number} timeout - The maximum amount of time (in ms) to wait for the event.
* @returns {Chainable<Subject>}
*/
waitForAmplitudeEvent(eventName: string, requiredProperties?: string[]): Chainable<Subject>
/**
* Intercepts a specific graphql operation and responds with the given fixture.
* @param {string} operationName - The name of the graphql operation to intercept.
* @param {string} fixturePath - The path to the fixture to respond with.
*/
interceptGraphqlOperation(operationName: string, fixturePath: string): Chainable<Subject>
/**
* Intercepts a quote request and responds with the given fixture.
* @param {string} fixturePath - The path to the fixture to respond with.
*/
interceptQuoteRequest(fixturePath: string): Chainable<Subject>
}
interface Cypress {
eagerlyConnect?: boolean
}
interface VisitOptions {
featureFlags?: Array<{ flag: FeatureFlags; value: boolean }>
/**
* Initial user state.
* @default {@type import('../utils/user-state').CONNECTED_WALLET_USER_STATE}
*/
userState?: Partial<UserState>
/**
* If false, prevents the app from eagerly connecting to the injected provider.
* @default true
*/
eagerlyConnect?: false
}
}
}
export function registerCommands() {
// sets up the injected provider to be a mock ethereum provider with the given mnemonic/index
// eslint-disable-next-line no-undef
Cypress.Commands.overwrite(
'visit',
(original, url: string | Partial<Cypress.VisitOptions>, options?: Partial<Cypress.VisitOptions>) => {
if (typeof url !== 'string') {
throw new Error('Invalid arguments. The first argument to cy.visit must be the path.')
}
// Parse overrides
const flagsOn: FeatureFlags[] = []
const flagsOff: FeatureFlags[] = []
options?.featureFlags?.forEach((f) => {
if (f.value) {
flagsOn.push(f.flag)
} else {
flagsOff.push(f.flag)
}
})
// Format into URL parameters
const overrideParams = new URLSearchParams()
if (flagsOn.length > 0) {
overrideParams.append(
'featureFlagOverride',
flagsOn.map((flag) => getFeatureFlagName(flag, FeatureFlagClient.Web)).join(','),
)
}
if (flagsOff.length > 0) {
overrideParams.append(
'featureFlagOverrideOff',
flagsOn.map((flag) => getFeatureFlagName(flag, FeatureFlagClient.Web)).join(','),
)
}
return cy.provider().then((provider) =>
original({
...options,
url:
[...overrideParams.entries()].length === 0
? url
: url.includes('?')
? `${url}&${overrideParams.toString()}`
: `${url}?${overrideParams.toString()}`,
onBeforeLoad(win) {
options?.onBeforeLoad?.(win)
setInitialUserState(win, {
...initialState,
...(options?.userState ?? {}),
})
win.ethereum = provider
win.Cypress.eagerlyConnect = options?.eagerlyConnect ?? true
win.localStorage.setItem(ALLOW_ANALYTICS_ATOM_KEY, 'true')
win.localStorage.setItem('showUniswapExtensionLaunchAtom', 'false')
},
}),
)
},
)
Cypress.Commands.add('waitForAmplitudeEvent', (eventName, requiredProperties) => {
function findAndDiscardEventsUpToTarget() {
const events = Cypress.env('amplitudeEventCache')
const targetEventIndex = events.findIndex((event) => {
if (event.event_type !== eventName) {
return false
}
if (requiredProperties) {
return requiredProperties.every((prop) => event.event_properties[prop])
}
return true
})
if (targetEventIndex !== -1) {
const event = events[targetEventIndex]
Cypress.env('amplitudeEventCache', events.slice(targetEventIndex + 1))
return cy.wrap(event)
} else {
// If not found, retry after waiting for more events to be sent.
return cy.wait('@amplitude').then(findAndDiscardEventsUpToTarget)
}
}
return findAndDiscardEventsUpToTarget()
})
Cypress.env('graphqlInterceptions', new Map())
Cypress.Commands.add('interceptGraphqlOperation', (operationName, fixturePath) => {
const graphqlInterceptions = Cypress.env('graphqlInterceptions')
cy.intercept(/(?:interface|beta)\.gateway\.uniswap\.org\/v1\/graphql/, (req) => {
req.headers['origin'] = 'https://app.uniswap.org'
const currentOperationName = req.body.operationName
if (graphqlInterceptions.has(currentOperationName)) {
const fixturePath = graphqlInterceptions.get(currentOperationName)
req.reply({ fixture: fixturePath })
} else {
req.continue()
}
}).as(operationName)
graphqlInterceptions.set(operationName, fixturePath)
})
Cypress.Commands.add('interceptQuoteRequest', (fixturePath) => {
return cy.intercept(/(?:interface|beta)\.gateway\.uniswap\.org\/v2\/quote/, (req) => {
req.headers['origin'] = 'https://app.uniswap.org'
req.reply({ fixture: fixturePath })
})
})
}