Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#github-username #743

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
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