diff --git a/ghost/admin/app/components/gh-nav-menu/footer.hbs b/ghost/admin/app/components/gh-nav-menu/footer.hbs index 36e77fef04..3845d9d8c7 100644 --- a/ghost/admin/app/components/gh-nav-menu/footer.hbs +++ b/ghost/admin/app/components/gh-nav-menu/footer.hbs @@ -23,7 +23,7 @@
{{#if (and this.whatsNew.hasNew (not this.whatsNew.hasNewFeatured))}}{{/if}}
- {{svg-jar "arrow-down" class="w3 mr1 fill-darkgrey"}} + {{svg-jar "arrow-down" class="w3 mr1 fill-darkgrey" data-test-nav="arrow-down"}} diff --git a/ghost/core/test/e2e-browser/portal/invites.spec.js b/ghost/core/test/e2e-browser/portal/invites.spec.js new file mode 100644 index 0000000000..5388e6e923 --- /dev/null +++ b/ghost/core/test/e2e-browser/portal/invites.spec.js @@ -0,0 +1,72 @@ +const {expect} = require('@playwright/test'); +const test = require('../fixtures/ghost-test'); +const security = require('@tryghost/security'); +const models = require('../../../core/server/models'); + +test.describe('Portal', () => { + test.describe('Invites', () => { + test('New staff member can signup using an invite link', async ({sharedPage}) => { + // Navigate to settings + await sharedPage.goto('/ghost'); + await sharedPage.locator('[data-test-nav="settings"]').click(); + await sharedPage.waitForLoadState('networkidle'); + + const testEmail = 'test@gmail.com'; + + // Send the invitation + await sharedPage.getByRole('button', {name: 'Invite people'}).click(); + await sharedPage.getByPlaceholder('jamie@example.com').fill(testEmail); + + // Set up response listener just before clicking send + const responsePromise = sharedPage.waitForResponse( + response => response.url().includes('/api/admin/invites/') && + response.request().method() === 'POST' + ); + + // Click send invitation + await sharedPage.getByRole('button', {name: 'Send invitation'}).click(); + + // Wait for the API response + const response = await responsePromise; + await response.json(); + + // Verify the invitation was sent (UI feedback) + const invitedMessage = sharedPage.getByText('Invitation sent', {exact: true}); + await expect(invitedMessage).toBeVisible({timeout: 5000}); + + // Get the token from database + const invite = await models.Invite.findOne({email: testEmail}); + const token = invite.get('token'); + + // Construct the invite URL + const adminUrl = new URL(sharedPage.url()).origin + '/ghost'; + const encodedToken = security.url.encodeBase64(token); + const inviteUrl = `${adminUrl}/signup/${encodedToken}/`; + + //signout current user + await sharedPage.goto('/ghost/#/dashboard'); + await sharedPage.waitForLoadState('networkidle'); + await sharedPage.locator('[data-test-nav="arrow-down"]').click(); + await sharedPage.getByRole('link', {name: 'Sign out'}).click(); + + // Open invite URL + await sharedPage.goto(inviteUrl); + + // Verify we're on the signup page + await sharedPage.waitForLoadState('networkidle'); + await expect(sharedPage.locator('text=Create your account.')).toBeVisible(); + + //Signup using the invite Link + await sharedPage.getByPlaceholder('Jamie Larson').fill('Test User'); + await sharedPage.getByPlaceholder('jamie@example.com').fill(testEmail); + await sharedPage.getByPlaceholder('At least 10 characters').fill('test123456'); + await sharedPage.getByRole('button', {name: 'Create Account →'}).click(); + await sharedPage.waitForLoadState('networkidle'); + await expect(sharedPage.locator('text=Start creating content.')).toBeVisible(); + await expect(sharedPage).toHaveURL('/ghost/#/posts'); + + await sharedPage.locator('[data-test-nav="arrow-down"]').click(); + await expect(sharedPage.locator(`text=${testEmail}`)).toBeVisible(); + }); + }); +});