Skip to content

Commit 0c69b9f

Browse files
committed
[APIM-11537] Add tests. Fix lint.
1 parent 0002324 commit 0c69b9f

File tree

10 files changed

+340
-62
lines changed

10 files changed

+340
-62
lines changed

gravitee-apim-portal-webui-next/src/app/log-in/log-in.component.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
}
4040
</mat-form-field>
4141
@if (error() === 401) {
42-
<mat-error class="log-in__form__error" i18n="@@logInError401">The username or password you entered is incorrect, please try again.</mat-error>
42+
<mat-error class="log-in__form__error" i18n="@@logInError401"
43+
>The username or password you entered is incorrect, please try again.</mat-error
44+
>
4345
}
4446
</div>
4547
<div class="log-in__form__buttons">

gravitee-apim-portal-webui-next/src/app/log-in/log-in.component.scss

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
.log-in {
2424
display: flex;
2525
flex-flow: column;
26-
gap: 20px;
2726
background: #{theme.$background-color};
27+
gap: 20px;
2828

2929
&__form {
3030
display: flex;
@@ -47,7 +47,7 @@
4747
}
4848

4949
&__content:last-child {
50-
padding: 0 20px 20px;
50+
padding: 0 20px 20px;
5151
}
5252

5353
&__fields {
@@ -79,8 +79,8 @@
7979
}
8080

8181
&__error {
82-
text-align: center;
8382
font-size: 14px;
83+
text-align: center;
8484
}
8585
}
8686

@@ -116,12 +116,12 @@
116116
}
117117

118118
&__idp {
119+
overflow: hidden;
119120
width: 100%;
120-
font-weight: bold;
121+
padding: 0 18px;
121122
color: #{theme.$button-text-text-color};
123+
font-weight: bold;
122124
white-space: nowrap;
123-
overflow: hidden;
124-
padding: 0 18px;
125125

126126
&__container {
127127
display: flex;
@@ -135,8 +135,8 @@
135135
}
136136

137137
::ng-deep .mdc-button__label {
138-
white-space: nowrap;
139138
overflow: hidden;
139+
white-space: nowrap;
140140
}
141141

142142
&__label {

gravitee-apim-portal-webui-next/src/app/log-in/log-in.component.spec.ts

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,41 @@ import { MatButtonHarness } from '@angular/material/button/testing';
2222
import { MatInputHarness } from '@angular/material/input/testing';
2323

2424
import { LogInComponent } from './log-in.component';
25-
import { AppTestingModule, TESTING_BASE_URL } from '../../testing/app-testing.module';
25+
import { IdentityProvider } from '../../entities/configuration/identity-provider';
26+
import { AuthService } from '../../services/auth.service';
27+
import { ConfigService } from '../../services/config.service';
28+
import { IdentityProviderService } from '../../services/identity-provider.service';
29+
import { AppTestingModule, ConfigServiceStub, IdentityProviderServiceStub, TESTING_BASE_URL } from '../../testing/app-testing.module';
30+
import { DivHarness } from '../../testing/div.harness';
2631

2732
describe('LogInComponent', () => {
2833
let fixture: ComponentFixture<LogInComponent>;
2934
let harnessLoader: HarnessLoader;
3035
let httpTestingController: HttpTestingController;
3136

37+
async function initComponent() {
38+
fixture = TestBed.createComponent(LogInComponent);
39+
harnessLoader = TestbedHarnessEnvironment.loader(fixture);
40+
httpTestingController = TestBed.inject(HttpTestingController);
41+
fixture.detectChanges();
42+
}
43+
44+
function enableLocalLogin(enable: boolean) {
45+
const configService = TestBed.inject(ConfigService) as unknown as ConfigServiceStub;
46+
configService.configuration.authentication!.localLogin!.enabled = enable;
47+
}
48+
49+
function initSsoProviders(providers: IdentityProvider[]) {
50+
const identityProviderService = TestBed.inject(IdentityProviderService) as unknown as IdentityProviderServiceStub;
51+
identityProviderService.providers = providers;
52+
}
53+
3254
beforeEach(async () => {
3355
await TestBed.configureTestingModule({
3456
imports: [LogInComponent, AppTestingModule],
3557
}).compileComponents();
3658

37-
fixture = TestBed.createComponent(LogInComponent);
38-
harnessLoader = TestbedHarnessEnvironment.loader(fixture);
39-
httpTestingController = TestBed.inject(HttpTestingController);
40-
fixture.detectChanges();
59+
await initComponent();
4160
});
4261

4362
afterEach(() => {
@@ -66,6 +85,7 @@ describe('LogInComponent', () => {
6685

6786
expect(await submitButton.isDisabled()).toEqual(true);
6887
});
88+
6989
it('should not validate form with missing password', async () => {
7090
const submitButton = await harnessLoader.getHarness(MatButtonHarness.with({ text: 'Log in' }));
7191

@@ -88,4 +108,81 @@ describe('LogInComponent', () => {
88108
httpTestingController.expectOne(`${TESTING_BASE_URL}/user`).flush({});
89109
httpTestingController.expectOne(`${TESTING_BASE_URL}/portal-menu-links`).flush({});
90110
});
111+
112+
it('should not display log-in form', async () => {
113+
enableLocalLogin(false);
114+
initSsoProviders([
115+
{
116+
id: 'github',
117+
name: 'GitHub',
118+
},
119+
]);
120+
121+
await initComponent();
122+
123+
const login = await harnessLoader.getHarnessOrNull(DivHarness.with({ selector: '.log-in__form' }));
124+
expect(login).toBeNull();
125+
126+
const orSeparator = await harnessLoader.getHarnessOrNull(DivHarness.with({ selector: '.log-in__sso__separator' }));
127+
expect(orSeparator).toBeNull();
128+
129+
const ssoProvider = await harnessLoader.getHarnessOrNull(MatButtonHarness.with({ selector: '.log-in__sso__idp' }));
130+
expect(ssoProvider).not.toBeNull();
131+
132+
const providerText = await ssoProvider!.getText();
133+
expect(providerText).toEqual('Continue with GitHub');
134+
});
135+
136+
it('should not display SSO providers', async () => {
137+
enableLocalLogin(true);
138+
initSsoProviders([]);
139+
140+
await initComponent();
141+
142+
const login = await harnessLoader.getHarnessOrNull(DivHarness.with({ selector: '.log-in__form' }));
143+
expect(login).not.toBeNull();
144+
145+
const orSeparator = await harnessLoader.getHarnessOrNull(DivHarness.with({ selector: '.log-in__sso__separator' }));
146+
expect(orSeparator).toBeNull();
147+
148+
const ssoProvider = await harnessLoader.getHarnessOrNull(MatButtonHarness.with({ selector: '.log-in__sso__idp' }));
149+
expect(ssoProvider).toBeNull();
150+
});
151+
152+
it('should display "or" separator and identity providers', async () => {
153+
const identityProviders = [
154+
{ id: 'github', name: 'GitHub' },
155+
{ id: 'google', name: 'Google' },
156+
{ id: 'graviteeio_am', name: 'Gravitee AM' },
157+
];
158+
enableLocalLogin(true);
159+
initSsoProviders(identityProviders);
160+
161+
await initComponent();
162+
163+
const orSeparator = await harnessLoader.getHarnessOrNull(DivHarness.with({ selector: '.log-in__sso__separator' }));
164+
expect(orSeparator).not.toBeNull();
165+
166+
const ssoProviders = await harnessLoader.getAllHarnesses(MatButtonHarness.with({ selector: '.log-in__sso__idp' }));
167+
const providerTexts = await Promise.all(ssoProviders.map(harness => harness.getText()));
168+
console.log('providerTexts', providerTexts);
169+
170+
for (const provider of identityProviders) {
171+
const found = providerTexts.find(text => text === `Continue with ${provider.name}`);
172+
expect(found).toBeDefined();
173+
}
174+
});
175+
176+
it('should redirect when clicked on SSO provider', async () => {
177+
enableLocalLogin(true);
178+
initSsoProviders([{ id: 'google', name: 'Google' }]);
179+
180+
const authenticateSSO = jest.spyOn(TestBed.inject(AuthService), 'authenticateSSO').mockReturnValue();
181+
182+
await initComponent();
183+
184+
const ssoProvider = await harnessLoader.getHarness(MatButtonHarness.with({ selector: '.log-in__sso__idp' }));
185+
await ssoProvider.click();
186+
expect(authenticateSSO).toHaveBeenCalledWith({ id: 'google', name: 'Google' }, '');
187+
});
91188
});

gravitee-apim-portal-webui-next/src/app/log-in/reset-password/reset-password-confirmation/reset-password-confirmation.component.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
<mat-card appearance="outlined" class="reset-password-confirmation">
1919
@if (!isSubmitted) {
2020
<mat-card-header class="reset-password-confirmation__header">
21-
<mat-card-title class="reset-password-confirmation__title" i18n="@@resetPasswordConfirmationTitle">Reset password confirmation</mat-card-title>
21+
<mat-card-title class="reset-password-confirmation__title" i18n="@@resetPasswordConfirmationTitle"
22+
>Reset password confirmation</mat-card-title
23+
>
2224
</mat-card-header>
2325
@if (!isTokenExpired && !!resetPasswordConfirmationForm) {
2426
<mat-card-content class="reset-password-confirmation__content">

gravitee-apim-portal-webui-next/src/app/log-in/reset-password/reset-password-confirmation/reset-password-confirmation.component.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
display: flex;
2525
width: 400px;
2626
flex-flow: column;
27-
gap: 20px;
2827
background: #{theme.$background-color};
28+
gap: 20px;
2929

3030
&__header {
3131
padding: 20px 20px 0;

gravitee-apim-portal-webui-next/src/app/log-in/reset-password/reset-password.component.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
display: flex;
2525
width: 400px;
2626
flex-flow: column;
27-
gap: 20px;
2827
background: #{theme.$background-color};
28+
gap: 20px;
2929

3030
&__form {
3131
display: flex;

gravitee-apim-portal-webui-next/src/components/nav-bar/nav-bar.component.spec.ts

Lines changed: 74 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ describe('NavBarComponent', () => {
3434
let harnessLoader: HarnessLoader;
3535
let componentRef: ComponentRef<NavBarComponent>;
3636
let httpTestingController: HttpTestingController;
37+
const customLinks = [
38+
{
39+
id: 'link-id-1',
40+
type: 'external',
41+
name: 'link-name-1',
42+
target: 'link-target-1',
43+
order: 1,
44+
},
45+
{
46+
id: 'link-id-2',
47+
type: 'external',
48+
name: 'link-name-2',
49+
target: 'link-target-2',
50+
order: 2,
51+
},
52+
];
3753

3854
const init = async (isMobile: boolean = false) => {
3955
const mockBreakpointObserver = {
@@ -69,24 +85,26 @@ describe('NavBarComponent', () => {
6985
expect(logInButton).toBeFalsy();
7086
});
7187

72-
it('should show custom links', async () => {
73-
const customLinks = [
74-
{
75-
id: 'link-id-1',
76-
type: 'external',
77-
name: 'link-name-1',
78-
target: 'link-target-1',
79-
order: 1,
80-
},
81-
{
82-
id: 'link-id-2',
83-
type: 'external',
84-
name: 'link-name-2',
85-
target: 'link-target-2',
86-
order: 2,
87-
},
88-
];
88+
it('should not show links if user is not connected and login is forced', async () => {
89+
componentRef.setInput('customLinks', customLinks);
90+
componentRef.setInput('forceLogin', true);
91+
const link1Anchor = await harnessLoader.getHarnessOrNull(MatButtonHarness.with({ text: 'link-name-1' }));
92+
expect(link1Anchor).not.toBeTruthy();
93+
const link2Anchor = await harnessLoader.getHarnessOrNull(MatButtonHarness.with({ text: 'link-name-2' }));
94+
expect(link2Anchor).not.toBeTruthy();
95+
});
8996

97+
it('should show links if user is connected and login is forced', async () => {
98+
componentRef.setInput('customLinks', customLinks);
99+
componentRef.setInput('forceLogin', true);
100+
componentRef.setInput('currentUser', fakeUser());
101+
const link1Anchor = await harnessLoader.getHarnessOrNull(MatButtonHarness.with({ text: 'link-name-1' }));
102+
expect(link1Anchor).toBeTruthy();
103+
const link2Anchor = await harnessLoader.getHarnessOrNull(MatButtonHarness.with({ text: 'link-name-2' }));
104+
expect(link2Anchor).toBeTruthy();
105+
});
106+
107+
it('should show custom links if login is not forced', async () => {
90108
componentRef.setInput('customLinks', customLinks);
91109
const link1Anchor = await harnessLoader.getHarnessOrNull(MatButtonHarness.with({ text: 'link-name-1' }));
92110
expect(link1Anchor).toBeTruthy();
@@ -116,27 +134,34 @@ describe('NavBarComponent', () => {
116134
expect(linkTexts).toEqual(['Homepage', 'Catalog', 'Guides', 'Sign in']);
117135
});
118136

119-
it('should show custom links', async () => {
120-
expectHomePage([]);
137+
it('should show logout button if user connected', async () => {
138+
expectHomePage();
121139
componentRef.setInput('currentUser', fakeUser());
140+
fixture.detectChanges();
141+
142+
const menuButton = await harnessLoader.getHarness(MatButtonHarness.with({ selector: '.mobile-menu__button' }));
143+
await menuButton.click();
144+
145+
const links: NodeList = fixture.debugElement.nativeElement.querySelectorAll('.mobile-menu__link');
146+
const linkTexts = Array.from(links).map((el: Node) => el.textContent?.trim());
147+
expect(linkTexts).toEqual(['Homepage', 'Catalog', 'Guides', 'Applications', 'Log out']);
148+
});
122149

123-
const customLinks = [
124-
{
125-
id: 'link-id-1',
126-
type: 'external',
127-
name: 'link-name-1',
128-
target: 'link-target-1',
129-
order: 1,
130-
},
131-
{
132-
id: 'link-id-2',
133-
type: 'external',
134-
name: 'link-name-2',
135-
target: 'link-target-2',
136-
order: 2,
137-
},
138-
];
150+
it('should not show menu if user is not connected and login is forced', async () => {
151+
expectHomePage([]);
139152
componentRef.setInput('customLinks', customLinks);
153+
componentRef.setInput('forceLogin', true);
154+
fixture.detectChanges();
155+
156+
const menuButton = await harnessLoader.getHarnessOrNull(MatButtonHarness.with({ selector: '.mobile-menu__button' }));
157+
expect(menuButton).not.toBeTruthy();
158+
});
159+
160+
it('should show links if user is connected and login is forced', async () => {
161+
expectHomePage([]);
162+
componentRef.setInput('currentUser', fakeUser());
163+
componentRef.setInput('customLinks', customLinks);
164+
componentRef.setInput('forceLogin', true);
140165
fixture.detectChanges();
141166

142167
const menuButton = await harnessLoader.getHarness(MatButtonHarness.with({ selector: '.mobile-menu__button' }));
@@ -148,6 +173,20 @@ describe('NavBarComponent', () => {
148173
expect(linkTexts).toEqual(['Catalog', 'Guides', 'link-name-1', 'link-name-2', 'Applications', 'Log out']);
149174
});
150175

176+
it('should show custom links if login is not forced', async () => {
177+
expectHomePage([]);
178+
componentRef.setInput('customLinks', customLinks);
179+
fixture.detectChanges();
180+
181+
const menuButton = await harnessLoader.getHarness(MatButtonHarness.with({ selector: '.mobile-menu__button' }));
182+
await menuButton.click();
183+
fixture.detectChanges();
184+
185+
const links: NodeList = fixture.debugElement.nativeElement.querySelectorAll('.mobile-menu__link');
186+
const linkTexts = Array.from(links).map((el: Node) => el.textContent?.trim());
187+
expect(linkTexts).toEqual(['Catalog', 'Guides', 'link-name-1', 'link-name-2', 'Sign in']);
188+
});
189+
151190
it('should close menu when clicking outside', async () => {
152191
componentRef.setInput('currentUser', fakeUser());
153192
expectHomePage();

0 commit comments

Comments
 (0)