2.18.0: New Invitation flow, refactored Database Webhooks, new ShadCN UI Components (#384)
* Streamlined invitations flow * Removed web hooks in favor of handling logic directly in server actions * Added new Shadcn UI Components
This commit is contained in:
committed by
GitHub
parent
195cf41680
commit
2e20d3e76f
@@ -70,8 +70,10 @@ test.describe('Admin', () => {
|
||||
// use the email as the filter text
|
||||
const filterText = testUserEmail;
|
||||
|
||||
await filterAccounts(page, filterText);
|
||||
await selectAccount(page, filterText);
|
||||
await expect(async () => {
|
||||
await filterAccounts(page, filterText);
|
||||
await selectAccount(page, filterText);
|
||||
}).toPass();
|
||||
});
|
||||
|
||||
test('ban user flow', async ({ page }) => {
|
||||
@@ -224,8 +226,10 @@ test.describe('Admin', () => {
|
||||
|
||||
await page.goto(`/admin/accounts`);
|
||||
|
||||
await filterAccounts(page, filterText);
|
||||
await selectAccount(page, filterText);
|
||||
await expect(async () => {
|
||||
await filterAccounts(page, filterText);
|
||||
await selectAccount(page, filterText);
|
||||
}).toPass();
|
||||
|
||||
await page.getByTestId('admin-impersonate-button').click();
|
||||
|
||||
@@ -283,8 +287,10 @@ test.describe('Team Account Management', () => {
|
||||
|
||||
await page.goto(`/admin/accounts`);
|
||||
|
||||
await filterAccounts(page, teamName);
|
||||
await selectAccount(page, teamName);
|
||||
await expect(async () => {
|
||||
await filterAccounts(page, teamName);
|
||||
await selectAccount(page, teamName);
|
||||
}).toPass();
|
||||
});
|
||||
|
||||
test('delete team account flow', async ({ page }) => {
|
||||
@@ -340,15 +346,13 @@ async function filterAccounts(page: Page, email: string) {
|
||||
}
|
||||
|
||||
async function selectAccount(page: Page, email: string) {
|
||||
await expect(async () => {
|
||||
const link = page
|
||||
.locator('tr', { hasText: email.split('@')[0] })
|
||||
.locator('a');
|
||||
const link = page
|
||||
.locator('tr', { hasText: email.split('@')[0] })
|
||||
.locator('a');
|
||||
|
||||
await expect(link).toBeVisible();
|
||||
await expect(link).toBeVisible();
|
||||
|
||||
await link.click();
|
||||
await link.click();
|
||||
|
||||
await page.waitForURL(/\/admin\/accounts\/[^\/]+/);
|
||||
}).toPass();
|
||||
await page.waitForURL(/\/admin\/accounts\/[^\/]+/);
|
||||
}
|
||||
|
||||
@@ -45,15 +45,7 @@ test.describe('Auth flow', () => {
|
||||
password: 'password',
|
||||
});
|
||||
|
||||
await page.waitForURL('**/home', {
|
||||
timeout: 5_000,
|
||||
});
|
||||
|
||||
expect(page.url()).toContain('/home');
|
||||
|
||||
await auth.signOut();
|
||||
|
||||
expect(page.url()).toContain('/');
|
||||
await page.waitForURL('**/home');
|
||||
});
|
||||
|
||||
test('will sign out using the dropdown', async ({ page }) => {
|
||||
@@ -99,151 +91,3 @@ test.describe('Protected routes', () => {
|
||||
expect(page.url()).toContain('/auth/sign-in?next=/home/settings');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Last auth method tracking', () => {
|
||||
let testEmail: string;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
testEmail = auth.createRandomEmail();
|
||||
|
||||
// First, sign up with password
|
||||
await auth.goToSignUp();
|
||||
|
||||
await auth.signUp({
|
||||
email: testEmail,
|
||||
password: 'password123',
|
||||
repeatPassword: 'password123',
|
||||
});
|
||||
|
||||
await auth.visitConfirmEmailLink(testEmail);
|
||||
await page.waitForURL('**/home', {
|
||||
timeout: 5_000,
|
||||
});
|
||||
|
||||
// Sign out
|
||||
await auth.signOut();
|
||||
await page.waitForURL('/');
|
||||
});
|
||||
|
||||
test('should show last used method hint on sign-in page after password sign-in', async ({
|
||||
page,
|
||||
}) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
// Go to sign-in page and check for last method hint
|
||||
await auth.goToSignIn();
|
||||
|
||||
// Check if the last used method hint is visible
|
||||
const lastMethodHint = page.locator('[data-test="last-auth-method-hint"]');
|
||||
await expect(lastMethodHint).toBeVisible();
|
||||
|
||||
// Verify it shows the correct method (password)
|
||||
const passwordMethodText = page.locator('text=email and password');
|
||||
await expect(passwordMethodText).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show existing account hint on sign-up page after previous sign-in', async ({
|
||||
page,
|
||||
}) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
// Go to sign-up page (user already signed in with password in previous test)
|
||||
await auth.goToSignUp();
|
||||
|
||||
// Check if the existing account hint is visible
|
||||
const existingAccountHint = page.locator(
|
||||
'[data-test="existing-account-hint"]',
|
||||
);
|
||||
|
||||
await expect(existingAccountHint).toBeVisible();
|
||||
});
|
||||
|
||||
test('should track method after successful sign-in', async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
// Clear cookies to simulate a fresh session
|
||||
await page.context().clearCookies();
|
||||
|
||||
// Sign in with the test email
|
||||
await auth.goToSignIn();
|
||||
|
||||
await auth.signIn({
|
||||
email: testEmail,
|
||||
password: 'password123',
|
||||
});
|
||||
|
||||
await page.waitForURL('**/home', {
|
||||
timeout: 5_000,
|
||||
});
|
||||
|
||||
// Sign out and check the method is still tracked
|
||||
await auth.signOut();
|
||||
await page.waitForURL('/');
|
||||
|
||||
// Go to sign-in page and check for last method hint
|
||||
await auth.goToSignIn();
|
||||
|
||||
// The hint should still be visible after signing in again
|
||||
const lastMethodHint = page.locator('[data-test="last-auth-method-hint"]');
|
||||
|
||||
await expect(lastMethodHint).toBeVisible();
|
||||
});
|
||||
|
||||
test('should clear localStorage after 30 days simulation', async ({
|
||||
page,
|
||||
}) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
// Go to sign-in page first
|
||||
await auth.goToSignIn();
|
||||
|
||||
// Simulate old timestamp (31 days ago) by directly modifying localStorage
|
||||
const thirtyOneDaysAgo = Date.now() - 31 * 24 * 60 * 60 * 1000;
|
||||
|
||||
await page.evaluate((timestamp) => {
|
||||
const oldAuthMethod = {
|
||||
method: 'password',
|
||||
email: 'old@example.com',
|
||||
timestamp: timestamp,
|
||||
};
|
||||
localStorage.setItem('auth_last_method', JSON.stringify(oldAuthMethod));
|
||||
}, thirtyOneDaysAgo);
|
||||
|
||||
// Reload the page to trigger the expiry check
|
||||
await page.reload();
|
||||
|
||||
// The hint should not be visible for expired data
|
||||
const lastMethodHint = page.locator('[data-test="last-auth-method-hint"]');
|
||||
await expect(lastMethodHint).not.toBeVisible();
|
||||
|
||||
// Verify localStorage was cleared
|
||||
const storedMethod = await page.evaluate(() => {
|
||||
return localStorage.getItem('auth_last_method');
|
||||
});
|
||||
|
||||
expect(storedMethod).toBeNull();
|
||||
});
|
||||
|
||||
test('should handle localStorage errors gracefully', async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
await auth.goToSignIn();
|
||||
|
||||
// Simulate corrupted localStorage data
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem('auth_last_method', 'invalid-json-data');
|
||||
});
|
||||
|
||||
// Reload the page
|
||||
await page.reload();
|
||||
|
||||
// Should not crash and not show the hint
|
||||
const lastMethodHint = page.locator('[data-test="last-auth-method-hint"]');
|
||||
await expect(lastMethodHint).not.toBeVisible();
|
||||
|
||||
// Page should still be functional
|
||||
await expect(page.locator('input[name="email"]')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,9 +29,7 @@ test.describe('Password Reset Flow', () => {
|
||||
subject: 'Reset your password',
|
||||
});
|
||||
|
||||
await page.waitForURL('/update-password', {
|
||||
timeout: 1000,
|
||||
});
|
||||
await page.waitForURL('/update-password');
|
||||
|
||||
await auth.updatePassword(newPassword);
|
||||
|
||||
@@ -44,7 +42,7 @@ test.describe('Password Reset Flow', () => {
|
||||
await page.waitForURL('/home');
|
||||
}).toPass();
|
||||
|
||||
await auth.signOut();
|
||||
await page.context().clearCookies();
|
||||
|
||||
await page.waitForURL('/');
|
||||
await page.goto('/auth/sign-in');
|
||||
|
||||
@@ -120,16 +120,6 @@ test.describe('Full Invitation Flow', () => {
|
||||
|
||||
await invitations.auth.visitConfirmEmailLink(firstEmail);
|
||||
|
||||
console.log(`Signing up with ${firstEmail} ...`);
|
||||
|
||||
await invitations.auth.signUp({
|
||||
email: firstEmail,
|
||||
password: 'password',
|
||||
repeatPassword: 'password',
|
||||
});
|
||||
|
||||
await invitations.auth.visitConfirmEmailLink(firstEmail);
|
||||
|
||||
console.log(`Accepting invitation as ${firstEmail}`);
|
||||
|
||||
await invitations.acceptInvitation();
|
||||
|
||||
@@ -37,14 +37,6 @@ async function setupTeamWithMember(page: Page, memberRole = 'member') {
|
||||
// Sign up with the new member email and accept the invitation
|
||||
await invitations.auth.visitConfirmEmailLink(memberEmail);
|
||||
|
||||
await invitations.auth.signUp({
|
||||
email: memberEmail,
|
||||
password: 'password',
|
||||
repeatPassword: 'password',
|
||||
});
|
||||
|
||||
await invitations.auth.visitConfirmEmailLink(memberEmail);
|
||||
|
||||
await invitations.acceptInvitation();
|
||||
|
||||
await invitations.teamAccounts.openAccountsSelector();
|
||||
@@ -248,9 +240,7 @@ test.describe('Team Ownership Transfer', () => {
|
||||
const memberRow = page.getByRole('row', { name: memberEmail });
|
||||
|
||||
// Check for the primary owner badge on the member's row
|
||||
await expect(memberRow.locator('text=Primary Owner')).toBeVisible({
|
||||
timeout: 5000,
|
||||
});
|
||||
await expect(memberRow.locator('text=Primary Owner')).toBeVisible();
|
||||
|
||||
// The original owner should no longer have the primary owner badge
|
||||
const ownerRow = page.getByRole('row', { name: ownerEmail.split('@')[0] });
|
||||
|
||||
@@ -56,16 +56,6 @@ test.describe('Team Invitation with MFA Flow', () => {
|
||||
|
||||
await auth.visitConfirmEmailLink('super-admin@makerkit.dev');
|
||||
|
||||
await page
|
||||
.locator('[data-test="existing-account-hint"]')
|
||||
.getByRole('link', { name: 'Already have an account?' })
|
||||
.click();
|
||||
|
||||
await auth.signIn({
|
||||
email: 'super-admin@makerkit.dev',
|
||||
password: 'testingpassword',
|
||||
});
|
||||
|
||||
// Complete MFA verification
|
||||
await expect(async () => {
|
||||
await auth.submitMFAVerification(AuthPageObject.MFA_KEY);
|
||||
|
||||
Reference in New Issue
Block a user