Skip to content

Commit ce49513

Browse files
committed
update ProviderButton to grab to param and append to login redirect
1 parent 0175fb7 commit ce49513

File tree

2 files changed

+152
-126
lines changed

2 files changed

+152
-126
lines changed
Lines changed: 113 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,140 @@
11
import { render, screen } from '@testing-library/react'
2+
import qs from 'qs'
23
import { MemoryRouter, Route } from 'react-router-dom'
34

5+
import config from 'config'
6+
7+
import { EnterpriseLoginProviders } from 'services/config/LoginProvidersQueryOpts'
48
import { ThemeContextProvider } from 'shared/ThemeContext'
59
import { LoginProvidersEnum } from 'shared/utils/loginProviders'
610

7-
import ProviderCard, { InternalProviderButton } from './ProviderCard'
8-
9-
const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => (
10-
<ThemeContextProvider>
11-
<MemoryRouter initialEntries={['/']}>
12-
<Route path="/">{children}</Route>
13-
</MemoryRouter>
14-
</ThemeContextProvider>
15-
)
16-
17-
describe('ProviderCard', () => {
18-
describe('Bitbucket', () => {
19-
describe('when system is configured with Bitbucket', () => {
20-
it('renders external login button', () => {
21-
render(
22-
<ProviderCard
23-
provider={LoginProvidersEnum.BITBUCKET}
24-
providers={['BITBUCKET']}
25-
/>,
26-
{ wrapper }
27-
)
28-
29-
const element = screen.getByRole('link', {
30-
name: 'Login via Bitbucket',
31-
})
32-
expect(element).toBeInTheDocument()
33-
expect(element).toHaveAttribute('href', '/login/bb')
34-
})
11+
import ProviderCard from './ProviderCard'
3512

36-
it('renders self hosted login link', () => {
37-
render(
38-
<ProviderCard
39-
provider={LoginProvidersEnum.BITBUCKET}
40-
providers={['BITBUCKET_SERVER']}
41-
/>,
42-
{ wrapper }
43-
)
13+
vi.mock('config')
14+
config.API_URL = 'secret-api-url'
4415

45-
const element = screen.getByRole('link', {
46-
name: 'Login via Bitbucket Server',
47-
})
48-
expect(element).toBeInTheDocument()
49-
expect(element).toHaveAttribute('href', '/login/bbs')
50-
})
51-
})
16+
const { location } = window
17+
beforeEach(() => {
18+
Object.defineProperty(window, 'location', {
19+
value: { ...location, protocol: 'http:', host: 'secret-api-url' },
5220
})
21+
})
5322

54-
describe('GitHub', () => {
55-
describe('when system is configured with GitHub', () => {
56-
it('renders external login button', () => {
57-
render(
58-
<ProviderCard
59-
provider={LoginProvidersEnum.GITHUB}
60-
providers={['GITHUB']}
61-
/>,
62-
{ wrapper }
63-
)
64-
65-
const element = screen.getByRole('link', { name: 'Login via GitHub' })
66-
expect(element).toBeInTheDocument()
67-
expect(element).toHaveAttribute('href', '/login/gh')
68-
})
23+
afterEach(() => {
24+
Object.defineProperty(window, 'location', { value: location })
25+
})
6926

70-
it('renders self hosted login link', () => {
71-
render(
72-
<ProviderCard
73-
provider={LoginProvidersEnum.GITHUB}
74-
providers={['GITHUB_ENTERPRISE']}
75-
/>,
76-
{ wrapper }
77-
)
27+
const wrapper =
28+
(initialEntries = '/'): React.FC<React.PropsWithChildren> =>
29+
({ children }) => (
30+
<ThemeContextProvider>
31+
<MemoryRouter initialEntries={[initialEntries]}>
32+
<Route path="/">{children}</Route>
33+
</MemoryRouter>
34+
</ThemeContextProvider>
35+
)
36+
37+
type LoginProviders = typeof LoginProvidersEnum
38+
39+
type Provider = {
40+
[K in keyof LoginProviders]: LoginProviders[K]
41+
}[keyof LoginProviders]
42+
43+
interface Case {
44+
provider: Provider
45+
name: string
46+
providers: Array<EnterpriseLoginProviders>
47+
to: string
48+
}
49+
50+
const cases: Case[] = [
51+
{
52+
provider: LoginProvidersEnum.BITBUCKET,
53+
name: LoginProvidersEnum.BITBUCKET.name,
54+
providers: ['BITBUCKET'],
55+
to: '/login/bb',
56+
},
57+
{
58+
provider: LoginProvidersEnum.BITBUCKET,
59+
name: LoginProvidersEnum.BITBUCKET.selfHostedName,
60+
providers: ['BITBUCKET_SERVER'],
61+
to: '/login/bbs',
62+
},
63+
{
64+
provider: LoginProvidersEnum.GITHUB,
65+
name: LoginProvidersEnum.GITHUB.name,
66+
providers: ['GITHUB'],
67+
to: '/login/gh',
68+
},
69+
{
70+
provider: LoginProvidersEnum.GITHUB,
71+
name: LoginProvidersEnum.GITHUB.selfHostedName,
72+
providers: ['GITHUB_ENTERPRISE'],
73+
to: '/login/ghe',
74+
},
75+
{
76+
provider: LoginProvidersEnum.GITLAB,
77+
name: LoginProvidersEnum.GITLAB.name,
78+
providers: ['GITLAB'],
79+
to: '/login/gl',
80+
},
81+
{
82+
provider: LoginProvidersEnum.GITLAB,
83+
name: LoginProvidersEnum.GITLAB.selfHostedName,
84+
providers: ['GITLAB_ENTERPRISE'],
85+
to: '/login/gle',
86+
},
87+
{
88+
provider: LoginProvidersEnum.OKTA,
89+
name: LoginProvidersEnum.OKTA.name,
90+
providers: ['OKTA'],
91+
to: '/login/okta',
92+
},
93+
]
7894

79-
const element = screen.getByRole('link', {
80-
name: 'Login via GitHub Enterprise',
95+
describe('ProviderCard', () => {
96+
describe.each(cases)('$name', ({ provider, providers, to, name }) => {
97+
describe('when system is configured with $providers.[0]', () => {
98+
it('renders the correct login button', () => {
99+
render(<ProviderCard provider={provider} providers={providers} />, {
100+
wrapper: wrapper(),
81101
})
82-
expect(element).toBeInTheDocument()
83-
expect(element).toHaveAttribute('href', '/login/ghe')
84-
})
85-
})
86-
})
87-
88-
describe('GitLab', () => {
89-
describe('when system is configured with GitLab', () => {
90-
it('renders external login button', () => {
91-
render(
92-
<ProviderCard
93-
provider={LoginProvidersEnum.GITLAB}
94-
providers={['GITLAB']}
95-
/>,
96-
{ wrapper }
97-
)
98-
99-
const element = screen.getByRole('link', { name: 'Login via GitLab' })
100-
expect(element).toBeInTheDocument()
101-
expect(element).toHaveAttribute('href', '/login/gl')
102-
})
103-
104-
it('renders self hosted login link', () => {
105-
render(
106-
<ProviderCard
107-
provider={LoginProvidersEnum.GITLAB}
108-
providers={['GITLAB_ENTERPRISE']}
109-
/>,
110-
{ wrapper }
111-
)
112102

113103
const element = screen.getByRole('link', {
114-
name: 'Login via GitLab CE/EE',
104+
name: `Login via ${name}`,
115105
})
116106
expect(element).toBeInTheDocument()
117-
expect(element).toHaveAttribute('href', '/login/gle')
107+
expect(element).toHaveAttribute('href', `secret-api-url${to}`)
118108
})
119109
})
120110
})
121111

122-
describe('Okta', () => {
123-
describe('when system is configured with Okta', () => {
124-
it('renders external login button', () => {
125-
render(
126-
<ProviderCard
127-
provider={LoginProvidersEnum.OKTA}
128-
providers={['OKTA']}
129-
/>,
130-
{ wrapper }
131-
)
112+
describe('when to param is provided', () => {
113+
it('appends redirect param to the login url', () => {
114+
const queryString = qs.stringify(
115+
{ to: '/gh/codecov/gazebo' },
116+
{ addQueryPrefix: true }
117+
)
118+
render(
119+
<ProviderCard
120+
provider={LoginProvidersEnum.BITBUCKET}
121+
providers={['BITBUCKET']}
122+
/>,
123+
{ wrapper: wrapper(`/login/bb${queryString}`) }
124+
)
132125

133-
const element = screen.getByRole('link', { name: 'Login via Okta' })
134-
expect(element).toBeInTheDocument()
135-
expect(element).toHaveAttribute('href', '/login/okta')
126+
const redirectURL = qs.stringify(
127+
{ to: `http://secret-api-url/bb${queryString}` },
128+
{ addQueryPrefix: true }
129+
)
130+
const element = screen.getByRole('link', {
131+
name: `Login via Bitbucket`,
136132
})
137-
})
138-
139-
it('InternalProviderButton returns null', () => {
140-
const { container } = render(
141-
<InternalProviderButton provider={LoginProvidersEnum.OKTA} />,
142-
{ wrapper }
133+
expect(element).toBeInTheDocument()
134+
expect(element).toHaveAttribute(
135+
'href',
136+
`secret-api-url/login/bb${redirectURL}`
143137
)
144-
145-
expect(container).toBeEmptyDOMElement()
146138
})
147139
})
148140
})

src/pages/EnterpriseLandingPage/ProviderCard/ProviderCard.tsx

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import qs from 'qs'
2+
13
import { EnterpriseLoginProviders } from 'services/config/LoginProvidersQueryOpts'
4+
import { useLocationParams } from 'services/navigation/useLocationParams'
25
import { Theme, useThemeContext } from 'shared/ThemeContext'
36
import {
47
loginProviderImage,
@@ -19,19 +22,26 @@ interface ProviderCardProps {
1922

2023
interface ExternalProviderButtonProps {
2124
provider: Provider
25+
queryString?: string
2226
}
2327

2428
const ExternalProviderButton: React.FC<ExternalProviderButtonProps> = ({
2529
provider,
30+
queryString,
2631
}) => {
32+
let to = undefined
33+
if (queryString) {
34+
to = `${window.location.protocol}//${window.location.host}/${provider.externalKey}${queryString}`
35+
}
36+
2737
return (
2838
<Button
2939
hook=""
3040
disabled={false}
3141
variant={provider.variant}
3242
to={{
3343
pageName: 'signIn',
34-
options: { provider: provider?.externalKey },
44+
options: { provider: provider?.externalKey, to },
3545
}}
3646
>
3747
Login via {provider.name}
@@ -41,23 +51,30 @@ const ExternalProviderButton: React.FC<ExternalProviderButtonProps> = ({
4151

4252
interface InternalProviderButtonProps {
4353
provider: Provider
54+
queryString?: string
4455
}
4556

46-
export const InternalProviderButton: React.FC<InternalProviderButtonProps> = ({
57+
const InternalProviderButton: React.FC<InternalProviderButtonProps> = ({
4758
provider,
59+
queryString,
4860
}) => {
4961
if (provider.name === LoginProvidersEnum.OKTA.name) {
5062
return null
5163
}
5264

65+
let to = undefined
66+
if (queryString) {
67+
to = `${window.location.protocol}//${window.location.host}/${provider.selfHostedKey}${queryString}`
68+
}
69+
5370
return (
5471
<Button
5572
disabled={false}
5673
hook=""
5774
variant={provider?.variant}
5875
to={{
5976
pageName: 'signIn',
60-
options: { provider: provider.selfHostedKey },
77+
options: { provider: provider.selfHostedKey, to },
6178
}}
6279
>
6380
Login via {provider.selfHostedName}
@@ -79,6 +96,17 @@ const ProviderCard: React.FC<ProviderCardProps> = ({ provider, providers }) => {
7996
| string
8097
| undefined
8198

99+
const { params } = useLocationParams()
100+
let queryString = undefined
101+
// @ts-expect-error useLocationParams needs to be typed
102+
if (params?.to) {
103+
queryString = qs.stringify(
104+
// @ts-expect-error useLocationParams needs to be typed
105+
{ to: params?.to },
106+
{ addQueryPrefix: true }
107+
)
108+
}
109+
82110
return (
83111
<div className="flex flex-col items-center">
84112
<div className="flex w-64 flex-row items-center justify-center gap-2 pb-2">
@@ -87,10 +115,16 @@ const ProviderCard: React.FC<ProviderCardProps> = ({ provider, providers }) => {
87115
</div>
88116
<div className="flex w-64 flex-col gap-2">
89117
{isExternalProvider ? (
90-
<ExternalProviderButton provider={provider} />
118+
<ExternalProviderButton
119+
provider={provider}
120+
queryString={queryString}
121+
/>
91122
) : null}
92123
{isInternalProvider ? (
93-
<InternalProviderButton provider={provider} />
124+
<InternalProviderButton
125+
provider={provider}
126+
queryString={queryString}
127+
/>
94128
) : null}
95129
</div>
96130
</div>

0 commit comments

Comments
 (0)