Skip to content

Commit

Permalink
Added e2e test for pending verification
Browse files Browse the repository at this point in the history
  • Loading branch information
paustint committed Nov 4, 2024
1 parent a937546 commit 8fc92a3
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 22 deletions.
58 changes: 36 additions & 22 deletions apps/jetstream-e2e/src/pageObjectModels/AuthenticationPage.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class AuthenticationPage {

readonly signInFromHomePageButton: Locator;
readonly signUpFromHomePageButton: Locator;
readonly signUpCtaFromHomePageButton: Locator;

readonly signInFromFormLink: Locator;
readonly signUpFromFormLink: Locator;
Expand Down Expand Up @@ -51,7 +52,8 @@ export class AuthenticationPage {
constructor(page: Page) {
this.page = page;
this.signInFromHomePageButton = page.getByRole('link', { name: 'Log in' });
this.signUpFromHomePageButton = page.getByRole('link', { name: 'Sign up' });
this.signUpFromHomePageButton = page.getByRole('link', { name: 'Sign up', exact: true });
this.signUpCtaFromHomePageButton = page.getByRole('link', { name: 'Sign up for a free account' });

this.signUpFromFormLink = page.getByText('Need to register? Sign up').getByRole('link', { name: 'Sign up' });
this.signInFromFormLink = page.getByText('Already have an account? Login').getByRole('link', { name: 'Login' });
Expand Down Expand Up @@ -82,7 +84,7 @@ export class AuthenticationPage {
async goToSignUp(viaHomePage = true) {
if (viaHomePage) {
await this.page.goto('/');
await this.signUpFromHomePageButton.first().click();
await this.signUpFromHomePageButton.click();
} else {
await this.page.goto(this.routes.signup());
}
Expand All @@ -91,7 +93,7 @@ export class AuthenticationPage {
async goToLogin(viaHomePage = true) {
if (viaHomePage) {
await this.page.goto('/');
await this.signInFromHomePageButton.first().click();
await this.signInFromHomePageButton.click();
} else {
await this.page.goto(this.routes.login());
}
Expand Down Expand Up @@ -121,7 +123,7 @@ export class AuthenticationPage {
return `PWD-${new Date().getTime()}!${randomBytes(8).toString('hex')}`;
}

async signUpAndVerifyEmail() {
async signUpWithoutEmailVerification() {
const email = this.generateTestEmail();
const name = this.generateTestName();
const password = this.generateTestPassword();
Expand All @@ -133,6 +135,19 @@ export class AuthenticationPage {
// ensure email verification was sent
await verifyEmailLogEntryExists(email, 'Verify your email');

return {
email,
name,
password,
};
}

async signUpAndVerifyEmail() {
const { email, name, password } = await this.signUpWithoutEmailVerification();

// ensure email verification was sent
await verifyEmailLogEntryExists(email, 'Verify your email');

// Get token from session
const { pendingVerification } = await getUserSessionByEmail(email);

Expand Down Expand Up @@ -165,15 +180,28 @@ export class AuthenticationPage {
// ensure email verification was sent
await verifyEmailLogEntryExists(email, 'Verify your identity');

await this.verifyEmail(email, rememberMe);
}

async loginAndVerifyTotp(email: string, password: string, secret: string, rememberMe = false) {
await this.fillOutLoginForm(email, password);

await expect(this.page.getByText('Enter your verification code from your authenticator app')).toBeVisible();

await this.verifyTotp(email, password, secret, rememberMe);
}

async verifyEmail(email: string, rememberMe = false) {
// Get token from session
const { pendingVerification } = await getUserSessionByEmail(email);

await expect(pendingVerification || []).toHaveLength(1);

if (pendingVerification[0].type !== '2fa-email') {
if (pendingVerification.some(({ type }) => type !== '2fa-email' && type !== 'email')) {
throw new Error('Expected email verification');
}
const { token } = pendingVerification[0];

const { token } = pendingVerification[0] as { token: string };

await this.verificationCodeInput.fill(token);
if (rememberMe) {
Expand All @@ -182,21 +210,12 @@ export class AuthenticationPage {
await this.continueButton.click();

await this.page.waitForURL(`**/app`);

return {
email,
password,
};
}

async loginAndVerifyTotp(email: string, password: string, secret: string, rememberMe = false) {
async verifyTotp(email: string, password: string, secret: string, rememberMe = false) {
const { decodeBase32IgnorePadding } = await import('@oslojs/encoding');
const { generateTOTP } = await import('@oslojs/otp');

await this.fillOutLoginForm(email, password);

await expect(this.page.getByText('Enter your verification code from your authenticator app')).toBeVisible();

const code = await generateTOTP(decodeBase32IgnorePadding(secret), 30, 6);

// Get token from session
Expand All @@ -216,11 +235,6 @@ export class AuthenticationPage {
await this.continueButton.click();

await this.page.waitForURL(`**/app`);

return {
email,
password,
};
}

async resetPassword(email: string) {
Expand Down Expand Up @@ -271,7 +285,7 @@ export class AuthenticationPage {
await this.goToSignUp();

await this.page.goto('/');
await this.signUpFromHomePageButton.first().click();
await this.signUpFromHomePageButton.click();

await expect(this.signInFromFormLink).toBeVisible();
await expect(this.forgotPasswordLink).toBeVisible();
Expand Down
50 changes: 50 additions & 0 deletions apps/jetstream-e2e/src/tests/authentication/login3.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { expect, test } from '../../fixtures/fixtures';

test.beforeEach(async ({ page }) => {
await page.goto('/');
});

test.describe.configure({ mode: 'parallel' });

// Reset storage state for this file to avoid being authenticated
test.use({ storageState: { cookies: [], origins: [] } });

test.describe('Login 3', () => {
test('Pending Email Verification should not allow any other activity', async ({ page, authenticationPage, apiRequestUtils }) => {
const { email, password } = await test.step('Sign up without email verification', async () => {
return await authenticationPage.signUpWithoutEmailVerification();
});

await test.step('Ensure clicking login shows email verification', async () => {
await page.goto('/');
await expect(authenticationPage.signInFromHomePageButton).toBeVisible();
await authenticationPage.signInFromHomePageButton.click();
await expect(page.getByText('Verify your email address')).toBeVisible();
});

await test.step('Ensure clicking sign up shows email verification', async () => {
await page.goto('/');
await expect(authenticationPage.signUpFromHomePageButton).toBeVisible();
await authenticationPage.signUpFromHomePageButton.click();
await expect(page.getByText('Verify your email address')).toBeVisible();
});

await test.step('Ensure clicking sign up from CTA shows email verification', async () => {
await page.goto('/');
await expect(authenticationPage.signUpCtaFromHomePageButton).toBeVisible();
await authenticationPage.signUpCtaFromHomePageButton.click();
await expect(page.getByText('Verify your email address')).toBeVisible();
});

await test.step('Ensure authenticated API fails prior to email verification', async () => {
const response = await apiRequestUtils.makeRequestRaw('GET', '/api/me', { Accept: 'application/json' });
await expect(response.status()).toBe(401);
});

await test.step('Verify email and ensure we can make an authenticated API request', async () => {
await authenticationPage.verifyEmail(email);
const response = await apiRequestUtils.makeRequestRaw('GET', '/api/me', { Accept: 'application/json' });
await expect(response.status()).toBe(200);
});
});
});

0 comments on commit 8fc92a3

Please sign in to comment.