Skip to content

Commit af30769

Browse files
authored
feat: add e2e tests for change-account and change-org dropdowns (#5429)
1 parent 0610276 commit af30769

File tree

4 files changed

+296
-46
lines changed

4 files changed

+296
-46
lines changed

cypress/e2e/cloud/globalHeader.ts

Lines changed: 257 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,293 @@
1-
describe('multi-account multi-org global header', () => {
1+
describe('change-account change-org global header', () => {
22
const globalHeaderFeatureFlags = {
33
quartzIdentity: true,
44
multiOrg: true,
55
}
66

7-
beforeEach(() => {
8-
// Maintain the same session for all tests so that further logins aren't required.
9-
Cypress.Cookies.preserveOnce('sid')
10-
})
7+
let idpeOrgID: string
8+
9+
const interceptPageReload = () => {
10+
cy.intercept('GET', 'api/v2/orgs').as('getOrgs')
11+
cy.intercept('GET', 'api/v2/flags').as('getFlags')
12+
cy.intercept('GET', 'api/v2/quartz/accounts/**/orgs').as('getQuartzOrgs')
13+
}
14+
15+
const makeQuartzUseIDPEOrgID = () => {
16+
cy.intercept('GET', 'api/v2/quartz/identity', req => {
17+
req.continue(res => {
18+
res.body.org.id = idpeOrgID
19+
})
20+
}).as('getQuartzIdentity')
21+
22+
cy.intercept('GET', '/api/v2/quartz/accounts/**/orgs', req => {
23+
req.continue(res => {
24+
res.body[0].id = idpeOrgID
25+
})
26+
}).as('getQuartzOrgs')
27+
28+
cy.intercept('GET', 'api/v2/quartz/orgs/*', req => {
29+
req.continue(res => {
30+
res.body.id = idpeOrgID
31+
})
32+
}).as('getQuartzOrgDetails')
33+
}
34+
35+
const mockQuartzOutage = () => {
36+
const quartzFailure = {
37+
statusCode: 503,
38+
body: 'Service Unavailable',
39+
}
40+
41+
cy.intercept('GET', 'api/v2/quartz/identity', quartzFailure).as(
42+
'getQuartzIdentity'
43+
)
44+
cy.intercept('GET', 'api/v2/quartz/accounts', quartzFailure).as(
45+
'getQuartzAccounts'
46+
)
47+
}
1148

1249
before(() => {
1350
cy.flush().then(() =>
1451
cy.signin().then(() => {
15-
cy.get('@org').then(() => {
16-
cy.getByTestID('home-page--header').should('be.visible')
17-
cy.setFeatureFlags(globalHeaderFeatureFlags).then(() => {
18-
// cy.wait is necessary to ensure sufficient time for the feature flag override.
19-
// We cannot cy.wait an intercepted route because this isn't a network request.
20-
cy.wait(400).then(() => {
21-
cy.visit('/')
22-
})
23-
})
52+
cy.request({
53+
method: 'GET',
54+
url: '/api/v2/orgs',
55+
}).then(res => {
56+
// Store the IDPE org ID so that it can be cloned when intercepting quartz.
57+
idpeOrgID = res.body.orgs[0].id
2458
})
2559
})
2660
)
2761
})
2862

29-
describe('user profile avatar', () => {
63+
beforeEach(() => {
64+
// Preserve one session throughout.
65+
Cypress.Cookies.preserveOnce('sid')
66+
})
67+
68+
describe('global change-account and change-org header', () => {
69+
it('does not render when API requests to quartz fail', () => {
70+
mockQuartzOutage()
71+
interceptPageReload()
72+
cy.setFeatureFlags(globalHeaderFeatureFlags).then(() => {
73+
cy.visit('/')
74+
cy.wait(['@getQuartzIdentity', '@getQuartzAccounts'])
75+
cy.getByTestID('global-header--container').should('not.exist')
76+
})
77+
})
78+
79+
describe('change org dropdown', () => {
80+
before(() => {
81+
cy.request({
82+
method: 'GET',
83+
url: '/api/v2/orgs',
84+
}).then(res => {
85+
// Retrieve the user's org ID from IDPE.
86+
idpeOrgID = res.body.orgs[0].id
87+
88+
cy.setFeatureFlags(globalHeaderFeatureFlags)
89+
cy.visit('/')
90+
})
91+
})
92+
93+
beforeEach(() => {
94+
// For each test, replace the org id served by quartz-mock with the IDPE org id.
95+
// This ensures that routes based on the current org id are compatible with quartz-mock.
96+
makeQuartzUseIDPEOrgID()
97+
// A short wait is needed to ensure we've completed trailing API calls. Can't consistently cy.wait one route,
98+
// as the problem call varies. This adds < 1s to a 35+ second run, which seems acceptable to combat flake.
99+
cy.wait(200)
100+
})
101+
102+
it('navigates to the org settings page', () => {
103+
cy.getByTestID('globalheader--org-dropdown')
104+
.should('be.visible')
105+
.click()
106+
cy.getByTestID('globalheader--org-dropdown-main').should('be.visible')
107+
cy.getByTestID('globalheader--org-dropdown-main-Settings')
108+
.should('be.visible')
109+
.click()
110+
111+
cy.location('pathname').should('eq', `/orgs/${idpeOrgID}/about`)
112+
cy.getByTestID('org-profile--panel')
113+
.should('be.visible')
114+
.and('contain', 'Organization Profile')
115+
})
116+
117+
it('navigates to the org members page', () => {
118+
cy.getByTestID('globalheader--org-dropdown')
119+
.should('be.visible')
120+
.click()
121+
cy.getByTestID('globalheader--org-dropdown-main').should('be.visible')
122+
cy.getByTestID('globalheader--org-dropdown-main-Members')
123+
.should('be.visible')
124+
.click()
125+
126+
cy.location('pathname').should('eq', `/orgs/${idpeOrgID}/users`)
127+
cy.getByTestID('tabs--container')
128+
.should('be.visible')
129+
.and('contain', 'Add a new user to your organization')
130+
})
131+
132+
it('navigates to the org usage page', () => {
133+
cy.getByTestID('globalheader--org-dropdown')
134+
.should('exist')
135+
.click()
136+
137+
cy.getByTestID('globalheader--org-dropdown-main').should('be.visible')
138+
cy.getByTestID('globalheader--org-dropdown-main-Usage')
139+
.should('be.visible')
140+
.click()
141+
cy.location('pathname').should('eq', `/orgs/${idpeOrgID}/usage`)
142+
cy.getByTestID('tabs--container')
143+
.should('be.visible')
144+
.and('contain', 'Billing Stats')
145+
})
146+
147+
it('can change change the active org', () => {
148+
cy.intercept('GET', 'auth/orgs/58fafbb4f68e05e5', {
149+
statusCode: 200,
150+
body: 'Reaching this page serves an org change in prod.',
151+
}).as('getNewOrg')
152+
153+
cy.getByTestID('globalheader--org-dropdown')
154+
.should('exist')
155+
.click()
156+
157+
cy.getByTestID('globalheader--org-dropdown-main').should('be.visible')
158+
cy.getByTestID('dropdown-item')
159+
.contains('Switch Organization')
160+
.should('be.visible')
161+
.click()
162+
163+
cy.getByTestID('globalheader--org-dropdown-typeahead')
164+
.should('be.visible')
165+
.type('g 5')
166+
167+
cy.getByTestID('globalheader--org-dropdown-main--contents')
168+
.contains('Org 5')
169+
.should('be.visible')
170+
.click()
171+
172+
cy.contains('Reaching this page serves an org change in prod.').should(
173+
'be.visible'
174+
)
175+
})
176+
})
177+
178+
describe('change account dropdown', () => {
179+
beforeEach(() => {
180+
makeQuartzUseIDPEOrgID()
181+
cy.setFeatureFlags(globalHeaderFeatureFlags)
182+
})
183+
184+
before(() => {
185+
cy.visit('/')
186+
})
187+
188+
it('navigates to the account settings page', () => {
189+
cy.getByTestID('globalheader--account-dropdown')
190+
.should('exist')
191+
.click()
192+
193+
cy.getByTestID('globalheader--account-dropdown-main').should(
194+
'be.visible'
195+
)
196+
197+
cy.getByTestID('globalheader--account-dropdown-main-Settings')
198+
.should('be.visible')
199+
.click()
200+
201+
cy.getByTestID('account-settings--header').should('be.visible')
202+
})
203+
204+
it('navigates to the account billing page', () => {
205+
cy.getByTestID('globalheader--account-dropdown')
206+
.should('exist')
207+
.click()
208+
209+
cy.getByTestID('globalheader--account-dropdown-main').should(
210+
'be.visible'
211+
)
212+
213+
cy.getByTestID('globalheader--account-dropdown-main-Billing')
214+
.should('be.visible')
215+
.click()
216+
})
217+
218+
it('can change change the active account', () => {
219+
cy.intercept('GET', 'auth/accounts/415', {
220+
statusCode: 200,
221+
body: 'Reaching this page serves an account change in prod.',
222+
}).as('getNewAccount')
223+
224+
cy.getByTestID('globalheader--account-dropdown')
225+
.should('exist')
226+
.click()
227+
228+
cy.getByTestID('globalheader--account-dropdown-main').should(
229+
'be.visible'
230+
)
231+
cy.getByTestID('dropdown-item')
232+
.contains('Switch Account')
233+
.should('be.visible')
234+
.click()
235+
236+
cy.getByTestID('globalheader--account-dropdown-typeahead')
237+
.should('be.visible')
238+
.type('gan')
239+
240+
cy.getByTestID('globalheader--account-dropdown-main--contents')
241+
.contains('Veganomicon')
242+
.should('be.visible')
243+
.click()
244+
245+
cy.contains(
246+
'Reaching this page serves an account change in prod.'
247+
).should('be.visible')
248+
})
249+
})
250+
})
251+
252+
describe('user profile avatar', {scrollBehavior: false}, () => {
253+
before(() => {
254+
interceptPageReload()
255+
makeQuartzUseIDPEOrgID()
256+
// A reset is required here because the prior test ends on a mocked-up page served by cy.intercept,
257+
// which stands in for the 'change account' page actually served by quartz in prod.
258+
cy.visit('/').then(() => {
259+
cy.wait(['@getOrgs', '@getFlags'])
260+
cy.setFeatureFlags(globalHeaderFeatureFlags).then(() => {
261+
cy.wait('@getQuartzOrgs')
262+
cy.visit('/')
263+
})
264+
})
265+
})
266+
30267
it('navigates to the `user profile` page', () => {
31268
cy.getByTestID('global-header--user-avatar')
32269
.should('be.visible')
33-
.click({scrollBehavior: false})
270+
.click()
34271

35272
cy.getByTestID('global-header--user-popover-profile-button')
36273
.should('be.visible')
37-
.click({scrollBehavior: false})
274+
.click()
38275

39276
cy.getByTestID('user-profile--page').should('be.visible')
40277

41278
cy.getByTestID('global-header--user-avatar')
42279
.should('be.visible')
43-
.click({scrollBehavior: false})
280+
.click()
44281
})
45282

46283
it('allows the user to log out', () => {
47284
cy.getByTestID('global-header--user-avatar')
48285
.should('be.visible')
49-
.click({scrollBehavior: false})
286+
.click()
50287

51288
cy.getByTestID('global-header--user-popover-logout-button')
52289
.should('be.visible')
53-
.click({
54-
scrollBehavior: false,
55-
})
290+
.click()
56291
// Logout in remocal looks like a 404 because there is no quartz. This tests the logout URL.
57292
cy.location('pathname').should('eq', '/logout')
58293
})

src/identity/components/GlobalHeader/AccountDropdown.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,14 @@ export const AccountDropdown: FC<Props> = ({
5959
mainMenuHeaderIcon={IconFont.Switch_New}
6060
mainMenuHeaderText="Switch Account"
6161
mainMenuOptions={accountMainMenu}
62+
mainMenuTestID="globalheader--account-dropdown-main"
6263
style={style}
64+
testID="globalheader--account-dropdown"
6365
typeAheadInputPlaceholder="Search Accounts"
6466
typeAheadMenuOptions={accountsList}
6567
typeAheadOnSelectOption={switchAccount}
6668
typeAheadSelectedOption={selectedAccount}
69+
typeAheadTestID="globalheader--account-dropdown-typeahead"
6770
/>
6871
)
6972
}

0 commit comments

Comments
 (0)