Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions e2e/tests/login.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import {test, expect} from '@playwright/test';
import {test, expect, request} from '@playwright/test';

test('matches the screenshot for unauthenticated user', async ({page}) => {
await page.goto('http://localhost:5555/');
Expand All @@ -31,18 +31,31 @@ test('can sign in and sign out user', async ({page}) => {
const popupPromise = page.waitForEvent('popup');
await page.goto('http://localhost:5555/');
await page.getByText('Log in').click();
await page.route('*//api.github.com/*', async route => {
// Mock successful login response
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
email: '[email protected]',
data: {
login: 'default_user',
},
}),
});
});
const popup = await popupPromise;

await popup.getByText('test user 1').waitFor();
await popup.getByText('test user 1').hover(); // Needed for Firefox for some reason.
await popup.getByText('test user 1').click();
await popup.getByText('default_user').waitFor();
await popup.getByText('default_user').hover(); // Needed for Firefox for some reason.
await popup.getByText('default_user').click();
await popup.waitForEvent('close');
const login = page.locator('webstatus-login');

const expectedEmail = '[email protected]';
const expectedUsername = 'default_user';

// Should have the email address
await expect(login).toContainText(expectedEmail);
// Should have the githubUsername = 'default_user'
await expect(login).toContainText(expectedUsername);

const header = page.locator('webstatus-header');
await expect(header).toHaveScreenshot('authenticated-header.png');
Expand Down
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@
"dependencies": {
"@lit/context": "^1.1.3",
"@lit/task": "^1.0.0",
"@octokit/rest": "^21.0.2",
"@shoelace-style/shoelace": "^2.18.0",
"@types/google.visualization": "^0.0.74",
"octokit": "^4.0.2",
"@vaadin/router": "^2.0.0",
"firebase": "^11.0.1",
"lit": "^3.2.1",
Expand Down
14 changes: 8 additions & 6 deletions frontend/src/static/js/components/webstatus-login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import {consume} from '@lit/context';
import {LitElement, type TemplateResult, html, nothing} from 'lit';
import {customElement, state} from 'lit/decorators.js';

import {User} from 'firebase/auth';
import {firebaseUserContext} from '../contexts/firebase-user-context.js';
import {
FirebaseUser,
firebaseUserContext,
} from '../contexts/firebase-user-context.js';
import {
AuthConfig,
firebaseAuthContext,
Expand All @@ -34,7 +36,7 @@ export class WebstatusLogin extends LitElement {

@consume({context: firebaseUserContext, subscribe: true})
@state()
user?: User;
user?: FirebaseUser;

handleLogInClick(authConfig: AuthConfig) {
if (this.user === undefined) {
Expand Down Expand Up @@ -72,14 +74,14 @@ export class WebstatusLogin extends LitElement {
}

renderAuthenticatedButton(
user: User,
authConfig: AuthConfig,
user: FirebaseUser,
authConfig: AuthConfig
): TemplateResult {
return html`
<sl-dropdown>
<sl-button slot="trigger" caret
><sl-icon slot="prefix" name="${authConfig.icon}"></sl-icon
>${user.email}</sl-button
>${user?.gitHubUsername}</sl-button
>
<sl-menu>
<sl-menu-item @click=${() => this.handleLogOutClick(authConfig)}
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/static/js/contexts/firebase-user-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ import {createContext} from '@lit/context';
import type {User} from 'firebase/auth';
export type {User} from 'firebase/auth';

export const firebaseUserContext = createContext<User | undefined>(
'firebase-user',
export const firebaseUserContext = createContext<FirebaseUser | undefined>(
'firebase-user'

);

type FirebaseUser = User & {
gitHubUsername: string | null;
};

export type {FirebaseUser};
38 changes: 32 additions & 6 deletions frontend/src/static/js/services/webstatus-firebase-auth-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import {customElement, property, state} from 'lit/decorators.js';
import {consume, provide} from '@lit/context';
import {Task} from '@lit/task';
import {
FirebaseApp,
firebaseAppContext,
Expand All @@ -31,9 +32,12 @@ import {
getAuth,
signInWithPopup,
} from 'firebase/auth';
import {User, firebaseUserContext} from '../contexts/firebase-user-context.js';
import {
FirebaseUser,
firebaseUserContext,
} from '../contexts/firebase-user-context.js';
import {ServiceElement} from './service-element.js';

import {Octokit} from '@octokit/rest';
interface FirebaseAuthSettings {
emulatorURL: string;
tenantID: string;
Expand All @@ -52,14 +56,21 @@ export class WebstatusFirebaseAuthService extends ServiceElement {
firebaseAuthConfig?: AuthConfig;

@provide({context: firebaseUserContext})
user?: User;
user?: FirebaseUser;

_loadingGithubUsername?: Task;

// Useful for testing
authInitializer: (app: FirebaseApp | undefined) => Auth = getAuth;

// Useful for testing
emulatorConnector: (auth: Auth, url: string) => void = connectAuthEmulator;

loadGithubUsername = async (token?: string) => {
const octokit = new Octokit({auth: token});
return await octokit.users.getAuthenticated();
};

initFirebaseAuth() {
if (this.firebaseApp) {
const auth = this.authInitializer(this.firebaseApp);
Expand All @@ -84,9 +95,24 @@ export class WebstatusFirebaseAuthService extends ServiceElement {
// Set up the callback that will detect when:
// 1. The user first logs in
// 2. Resuming a session
this.firebaseAuthConfig.auth.onAuthStateChanged(user => {
this.user = user ? user : undefined;
});
this.firebaseAuthConfig.auth.onAuthStateChanged(
async (authenticatedUser?: any) => {
if (authenticatedUser !== null) {
await this.loadGithubUsername(authenticatedUser.accessToken)
.then(githubUser => {
this.user = {
...authenticatedUser,
gitHubUsername: githubUser.data.login || null,
};
})
.catch(_ => {
throw new Error('Github username request failed.');
});
} else {
throw new Error('No user authenticated.');
}
}
);
}
}

Expand Down
Loading