diff --git a/ghost/core/test/e2e-browser/portal/offers.spec.js b/ghost/core/test/e2e-browser/portal/offers.spec.js index 0b1e3a6241..f6519cf263 100644 --- a/ghost/core/test/e2e-browser/portal/offers.spec.js +++ b/ghost/core/test/e2e-browser/portal/offers.spec.js @@ -28,26 +28,31 @@ test.describe('Portal', () => { // check that offer was added in the offer list screen await sharedPage.goto('/ghost'); await sharedPage.locator('[data-test-nav="settings"]').click(); - await expect(sharedPage.getByTestId('offers')).toContainText(offerName); + await expect(await sharedPage.getByTestId('offers')).toContainText(offerName); await sharedPage.goto(offerLink); + // Wait for the load state to ensure the page has loaded completely const portalTriggerButton = await sharedPage.frameLocator('[data-testid="portal-trigger-frame"]').locator('[data-testid="portal-trigger-button"]'); - const portalFrame = await sharedPage.frameLocator('[data-testid="portal-popup-frame"]'); + // Wait for the iframe to be attached to the DOM + await sharedPage.waitForSelector('[data-testid="portal-popup-frame"]', {state: 'attached'}); - // check offer title is shown on portal - await expect(portalFrame.locator('.gh-portal-offer-title'), 'URL should open Portal with free-trial offer').toBeVisible(); - await expect(portalFrame.getByRole('heading', {name: offerName}), 'URL should open Portal with free-trial offer').toBeVisible(); + // Use the frameLocator to interact with elements inside the frame + const portalFrameLocator = await sharedPage.frameLocator('[data-testid="portal-popup-frame"]'); + await portalFrameLocator.locator('.gh-portal-offer-title').waitFor(); + + await expect(await portalFrameLocator.locator('.gh-portal-offer-title'), 'URL should open Portal with free-trial offer').toBeVisible(); + await expect(await portalFrameLocator.getByRole('heading', {name: offerName}), 'URL should open Portal with free-trial offer').toBeVisible(); // fill member details and click start trial - await portalFrame.locator('[data-test-input="input-name"]').fill('Testy McTesterson'); - await portalFrame.locator('[data-test-input="input-email"]').fill('testy+trial@example.com'); - await portalFrame.getByRole('button', {name: 'Start 14-day free trial'}).click(); + await portalFrameLocator.locator('[data-test-input="input-name"]').fill('Testy McTesterson'); + await portalFrameLocator.locator('[data-test-input="input-email"]').fill('testy+trial@example.com'); + await portalFrameLocator.getByRole('button', {name: 'Start 14-day free trial'}).click(); // handle newsletter selection page if it opens and click continue - const hasContinueBtn = await portalFrame.locator('text="Continue"').isVisible(); + const hasContinueBtn = await portalFrameLocator.locator('text="Continue"').isVisible(); if (hasContinueBtn) { - await portalFrame.getByRole('button', {name: 'Continue'}).click(); + await portalFrameLocator.getByRole('button', {name: 'Continue'}).click(); } // complete subscription @@ -57,20 +62,20 @@ test.describe('Portal', () => { await portalTriggerButton.click(); // check portal shows free trial info - await expect(portalFrame.locator('text=Free Trial – Ends'), 'Portal should show free trial info').toBeVisible(); + await expect(portalFrameLocator.locator('text=Free Trial – Ends'), 'Portal should show free trial info').toBeVisible(); // go to member list on admin await sharedPage.goto('/ghost'); await sharedPage.locator('.gh-nav a[href="#/members/"]').click(); // // 1 member, should be Testy, on Portal Tier - await expect(sharedPage.getByRole('link', {name: 'Testy McTesterson testy+trial@example.com'}), 'Should have 1 paid member').toBeVisible(); - await expect(sharedPage.getByRole('link', {name: tierName}), `Paid member should be on ${tierName}`).toBeVisible(); + await expect(await sharedPage.getByRole('link', {name: 'Testy McTesterson testy+trial@example.com'}), 'Should have 1 paid member').toBeVisible(); + await expect(await sharedPage.getByRole('link', {name: tierName}), `Paid member should be on ${tierName}`).toBeVisible(); // // Ensure the offer redemption count was bumped await sharedPage.goto('/ghost/#/settings/offers'); // await sharedPage.locator('.gh-nav a[href="#/offers/"]').click(); - const locator = sharedPage.locator(`[data-test-offer="${offerName}"]`); + const locator = await sharedPage.locator(`[data-test-offer="${offerName}"]`); await expect(locator).toContainText('1 redemption'); }); @@ -105,21 +110,29 @@ test.describe('Portal', () => { // fetch offer url from portal settings and open it await sharedPage.goto(offerLink); + // Wait for the load state to ensure the page has loaded completely + await sharedPage.waitForLoadState('load'); + const portalTriggerButton = await sharedPage.frameLocator('[data-testid="portal-trigger-frame"]').locator('[data-testid="portal-trigger-button"]'); - const portalFrame = await sharedPage.frameLocator('[data-testid="portal-popup-frame"]'); + // Wait for the iframe to be attached to the DOM + await sharedPage.waitForSelector('[data-testid="portal-popup-frame"]', {state: 'attached'}); + + // Use the frameLocator to interact with elements inside the frame + const portalFrameLocator = await sharedPage.frameLocator('[data-testid="portal-popup-frame"]'); + await portalFrameLocator.locator('.gh-portal-offer-title').waitFor(); // check offer title is visible on portal page - await expect(portalFrame.locator('.gh-portal-offer-title'), 'URL should open Portal with discount offer').toBeVisible(); + await expect(await portalFrameLocator.locator('.gh-portal-offer-title'), 'URL should open Portal with discount offer').toBeVisible(); // fill member details and continue - await portalFrame.locator('#input-name').fill('Testy McTesterson'); - await portalFrame.locator('#input-email').fill('testy+oneoff@example.com'); - await portalFrame.getByRole('button', {name: 'Continue'}).click(); + await portalFrameLocator.locator('#input-name').fill('Testy McTesterson'); + await portalFrameLocator.locator('#input-email').fill('testy+oneoff@example.com'); + await portalFrameLocator.getByRole('button', {name: 'Continue'}).click(); // check if newsletter selection screen is shown and continue - const hasContinueBtn = await portalFrame.locator('text="Continue"').isVisible(); + const hasContinueBtn = await portalFrameLocator.locator('text="Continue"').isVisible(); if (hasContinueBtn) { - await portalFrame.getByRole('button', {name: 'Continue'}).click(); + await portalFrameLocator.getByRole('button', {name: 'Continue'}).click(); } // complete stripe subscription @@ -128,15 +141,15 @@ test.describe('Portal', () => { // wait for site to load and open portal await portalTriggerButton.click(); // Discounted price should not be visible for member for one-time offers - await expect(portalFrame.locator('text=$5.40/month'), 'Portal should not show discounted price').not.toBeVisible(); + await expect(await portalFrameLocator.locator('text=$5.40/month'), 'Portal should not show discounted price').not.toBeVisible(); // go to members list on admin await sharedPage.goto('/ghost'); await sharedPage.locator('.gh-nav a[href="#/members/"]').click(); // 1 member, should be Testy, on Portal Tier - await expect(sharedPage.getByRole('link', {name: 'Testy McTesterson testy+oneoff@example.com'}), 'Should have 1 paid member').toBeVisible(); - await expect(sharedPage.getByRole('link', {name: tierName}), `Paid member should be on ${tierName}`).toBeVisible(); + await expect(await sharedPage.getByRole('link', {name: 'Testy McTesterson testy+oneoff@example.com'}), 'Should have 1 paid member').toBeVisible(); + await expect(await sharedPage.getByRole('link', {name: tierName}), `Paid member should be on ${tierName}`).toBeVisible(); }); test('Creates and uses a multiple-months discount Offer', async ({sharedPage}) => { @@ -164,27 +177,35 @@ test.describe('Portal', () => { await sharedPage.goto('/ghost'); await sharedPage.locator('[data-test-nav="settings"]').click(); - await expect(sharedPage.getByTestId('offers')).toContainText(offerName); + await expect(await sharedPage.getByTestId('offers')).toContainText(offerName); await sharedPage.goto(offerLink); + // Wait for the load state to ensure the page has loaded completely + await sharedPage.waitForLoadState('load'); + const portalTriggerButton = await sharedPage.frameLocator('[data-testid="portal-trigger-frame"]').locator('[data-testid="portal-trigger-button"]'); - const portalFrame = await sharedPage.frameLocator('[data-testid="portal-popup-frame"]'); + // Wait for the iframe to be attached to the DOM + await sharedPage.waitForSelector('[data-testid="portal-popup-frame"]', {state: 'attached'}); + + // Use the frameLocator to interact with elements inside the frame + const portalFrameLocator = await sharedPage.frameLocator('[data-testid="portal-popup-frame"]'); + await portalFrameLocator.locator('.gh-portal-offer-title').waitFor(); // check offer details are shown on portal page - await expect(portalFrame.locator('.gh-portal-offer-title'), 'URL should open Portal with discount offer').toBeVisible(); - await expect(portalFrame.locator('text=10% off for first 3 months.'), 'URL should open Portal with discount offer').toBeVisible(); - await expect(portalFrame.locator('text=$5.40'), 'URL should open Portal with discount offer').toBeVisible(); + await expect(await portalFrameLocator.locator('.gh-portal-offer-title'), 'URL should open Portal with discount offer').toBeVisible(); + await expect(await portalFrameLocator.locator('text=10% off for first 3 months.'), 'URL should open Portal with discount offer').toBeVisible(); + await expect(await portalFrameLocator.locator('text=$5.40'), 'URL should open Portal with discount offer').toBeVisible(); // fill member details and continue - await portalFrame.locator('#input-name').fill('Testy McTesterson'); - await portalFrame.locator('#input-email').fill('testy+multi@example.com'); - await portalFrame.getByRole('button', {name: 'Continue'}).click(); + await portalFrameLocator.locator('#input-name').fill('Testy McTesterson'); + await portalFrameLocator.locator('#input-email').fill('testy+multi@example.com'); + await portalFrameLocator.getByRole('button', {name: 'Continue'}).click(); // check newsletter selection if shown and continue - const hasContinueBtn = await portalFrame.locator('text="Continue"').isVisible(); + const hasContinueBtn = await portalFrameLocator.locator('text="Continue"').isVisible(); if (hasContinueBtn) { - await portalFrame.getByRole('button', {name: 'Continue'}).click(); + await portalFrameLocator.getByRole('button', {name: 'Continue'}).click(); } // complete stripe subscription @@ -194,13 +215,13 @@ test.describe('Portal', () => { await portalTriggerButton.click(); // Discounted price should not be visible for member for one-time offers - await expect(portalFrame.locator('text=$5.40/month'), 'Portal should show discounted price').toBeVisible(); + await expect(await portalFrameLocator.locator('text=$5.40/month'), 'Portal should show discounted price').toBeVisible(); await sharedPage.goto('/ghost'); await sharedPage.locator('.gh-nav a[href="#/members/"]').click(); // 1 member, should be Testy, on Portal Tier - await expect(sharedPage.getByRole('link', {name: 'Testy McTesterson testy+multi@example.com'}), 'Should have 1 paid member').toBeVisible(); - await expect(sharedPage.getByRole('link', {name: tierName}), `Paid member should be on ${tierName}`).toBeVisible(); + await expect(await sharedPage.getByRole('link', {name: 'Testy McTesterson testy+multi@example.com'}), 'Should have 1 paid member').toBeVisible(); + await expect(await sharedPage.getByRole('link', {name: tierName}), `Paid member should be on ${tierName}`).toBeVisible(); }); test('Creates and uses a forever discount Offer', async ({sharedPage}) => { @@ -232,23 +253,31 @@ test.describe('Portal', () => { await sharedPage.goto(offerLink); + // Wait for the load state to ensure the page has loaded completely + await sharedPage.waitForLoadState('load'); + const portalTriggerButton = await sharedPage.frameLocator('[data-testid="portal-trigger-frame"]').locator('[data-testid="portal-trigger-button"]'); - const portalFrame = await sharedPage.frameLocator('[data-testid="portal-popup-frame"]'); + // Wait for the iframe to be attached to the DOM + await sharedPage.waitForSelector('[data-testid="portal-popup-frame"]', {state: 'attached'}); + + // Use the frameLocator to interact with elements inside the frame + const portalFrameLocator = await sharedPage.frameLocator('[data-testid="portal-popup-frame"]'); + await portalFrameLocator.locator('.gh-portal-offer-title').waitFor(); // check offer details are shown on portal page - await expect(portalFrame.locator('.gh-portal-offer-title'), 'URL should open Portal with discount offer').toBeVisible(); - await expect(portalFrame.locator('text=10% off forever.'), 'URL should open Portal with discount offer').toBeVisible(); - await expect(portalFrame.locator('text=$5.40'), 'URL should open Portal with discount offer').toBeVisible(); + await expect(await portalFrameLocator.locator('.gh-portal-offer-title'), 'URL should open Portal with discount offer').toBeVisible(); + await expect(await portalFrameLocator.locator('text=10% off forever.'), 'URL should open Portal with discount offer').toBeVisible(); + await expect(await portalFrameLocator.locator('text=$5.40'), 'URL should open Portal with discount offer').toBeVisible(); // fill member details and continue - await portalFrame.locator('#input-name').fill('Testy McTesterson'); - await portalFrame.locator('#input-email').fill('testy+forever@example.com'); - await portalFrame.getByRole('button', {name: 'Continue'}).click(); + await portalFrameLocator.locator('#input-name').fill('Testy McTesterson'); + await portalFrameLocator.locator('#input-email').fill('testy+forever@example.com'); + await portalFrameLocator.getByRole('button', {name: 'Continue'}).click(); // check if newsletter selection page is shown and continue - const hasContinueBtn = await portalFrame.locator('text="Continue"').isVisible(); + const hasContinueBtn = await portalFrameLocator.locator('text="Continue"').isVisible(); if (hasContinueBtn) { - await portalFrame.getByRole('button', {name: 'Continue'}).click(); + await portalFrameLocator.getByRole('button', {name: 'Continue'}).click(); } await completeStripeSubscription(sharedPage); @@ -256,13 +285,13 @@ test.describe('Portal', () => { await portalTriggerButton.click(); // Discounted price should be visible for member for forever offers - await expect(portalFrame.locator('text=$5.40/month'), 'Portal should show discounted price').toBeVisible(); + await expect(portalFrameLocator.locator('text=$5.40/month'), 'Portal should show discounted price').toBeVisible(); await sharedPage.goto('/ghost'); await sharedPage.locator('.gh-nav a[href="#/members/"]').click(); // 1 member, should be Testy, on Portal Tier - await expect(sharedPage.getByRole('link', {name: 'Testy McTesterson testy+forever@example.com'}), 'Should have 1 paid member').toBeVisible(); - await expect(sharedPage.getByRole('link', {name: tierName}), `Paid member should be on ${tierName}`).toBeVisible(); + await expect(await sharedPage.getByRole('link', {name: 'Testy McTesterson testy+forever@example.com'}), 'Should have 1 paid member').toBeVisible(); + await expect(await sharedPage.getByRole('link', {name: tierName}), `Paid member should be on ${tierName}`).toBeVisible(); }); test('Archiving an offer', async ({sharedPage}) => { diff --git a/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js b/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js index c05fb146ac..a4bec039e3 100644 --- a/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js +++ b/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js @@ -271,7 +271,7 @@ const createOffer = async (page, {name, tierName, offerType, amount, discountTyp await page.getByTestId('offer-item').nth(0).click(); await page.getByRole('button', {name: 'Archive offer'}).click(); - const confirmModal = page.getByTestId('confirmation-modal'); + const confirmModal = await page.getByTestId('confirmation-modal'); await confirmModal.getByRole('button', {name: 'Archive'}).click(); } @@ -320,7 +320,7 @@ const fillInputIfExists = async (page, selector, value) => { const completeStripeSubscription = async (page) => { await page.locator('#cardNumber').fill('4242 4242 4242 4242'); - await page.locator('#cardExpiry').fill('04 / 24'); + await page.locator('#cardExpiry').fill('04 / 26'); await page.locator('#cardCvc').fill('424'); await page.locator('#billingName').fill('Testy McTesterson'); await page.getByRole('combobox', {name: 'Country or region'}).selectOption('US');