| import { test, expect, Page, Locator } from '@playwright/test' |
| import { setupSite, populateStats, addGoal } from '../fixtures' |
| import { tabButton } from '../test-utils' |
|
|
| const getReport = (page: Page) => page.getByTestId('report-behaviours') |
| const getExplorationTabButton = (report: Locator) => |
| tabButton(report, 'Explore') |
|
|
| test('load user journey', async ({ page, request }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: [ |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/page', |
| timestamp: { daysAgo: 7 } |
| } |
| ] |
| }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| await expect( |
| report.getByTestId('exploration-direction-forward') |
| ).toBeVisible() |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
| const secondColumn = report.getByTestId('exploration-column-1') |
|
|
| await expect(firstColumn).toBeVisible() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/page']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1']) |
|
|
| await expect(secondColumn).toHaveText(/1 step after/) |
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
|
|
| await firstColumn.getByRole('button', { name: '/page' }).click() |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/page' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect(secondColumn).toHaveText(/No further steps found/) |
| }) |
|
|
| test('load user journey and switch to a period with no events without waiting for load', async ({ |
| page, |
| request |
| }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: [ |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/page', |
| timestamp: { hoursAgo: 40 } |
| } |
| ] |
| }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| |
| await page.keyboard.press('d') |
|
|
| await expect(report).toHaveText(/No data yet/) |
| }) |
|
|
| test('load user journey and switch to a period with no events', async ({ |
| page, |
| request |
| }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: [ |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/page', |
| timestamp: { daysAgo: 7 } |
| } |
| ] |
| }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
|
|
| await expect(firstColumn).toBeVisible() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/page']) |
|
|
| |
| await page.keyboard.press('d') |
|
|
| await expect(report).toHaveText(/No data yet/) |
| }) |
|
|
| test('explore a 3-step funnel', async ({ page, request }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: [ |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/dashboard', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 125, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/another', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/journey', |
| timestamp: { minutesAgo: 35 } |
| } |
| ] |
| }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| await expect( |
| report.getByTestId('exploration-direction-forward') |
| ).toBeVisible() |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
| const secondColumn = report.getByTestId('exploration-column-1') |
| const thirdColumn = report.getByTestId('exploration-column-2') |
|
|
| await firstColumn.getByRole('button', { name: '/home' }).click() |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/home' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
|
|
| await secondColumn.getByRole('button', { name: '/login' }).click() |
|
|
| await expect( |
| secondColumn.getByRole('button', { name: '/login' }) |
| ).toHaveAttribute('data-exploration-step', '1') |
|
|
| await thirdColumn.getByRole('button', { name: '/dashboard' }).click() |
|
|
| await expect( |
| thirdColumn.getByRole('button', { name: '/dashboard' }) |
| ).toHaveAttribute('data-exploration-step', '2') |
|
|
| await expect(firstColumn).toBeVisible() |
| await expect(secondColumn).toBeVisible() |
| await expect(thirdColumn).toBeVisible() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home', '/login', '/another', '/dashboard', '/journey']) |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/home' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['3', '2', '1', '1', '1']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login', 'No further action']) |
|
|
| await expect( |
| secondColumn.getByRole('button', { name: '/login' }) |
| ).toHaveAttribute('data-exploration-step', '1') |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['2', '1']) |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['No further action', '/dashboard']) |
|
|
| await expect( |
| thirdColumn.getByRole('button', { name: '/dashboard' }) |
| ).toHaveAttribute('data-exploration-step', '2') |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1', '1']) |
|
|
| await expect(secondColumn).toHaveText(/1 step after/) |
| await expect(thirdColumn).toHaveText(/2 steps after/) |
|
|
| await test.step('Deselecting all resets the journey', async () => { |
| await report.getByTestId('exploration-deselect-all').click() |
|
|
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
| await expect(thirdColumn).toHaveText(/Select an event to continue/) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home', '/login', '/another', '/dashboard', '/journey']) |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/dashboard' }) |
| ).not.toHaveAttribute('data-exploration-step') |
| }) |
| }) |
|
|
| test('select entries in a 1-step journey', async ({ page, request }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: [ |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/dashboard', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 125, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/another-home', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/journey', |
| timestamp: { minutesAgo: 35 } |
| } |
| ] |
| }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| await expect( |
| report.getByTestId('exploration-direction-forward') |
| ).toBeVisible() |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
| const secondColumn = report.getByTestId('exploration-column-1') |
|
|
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home', '/login', '/another-home', '/dashboard', '/journey']) |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/home' }) |
| ).not.toHaveAttribute('data-exploration-step', '0') |
|
|
| await test.step('select an entry', async () => { |
| await firstColumn.getByRole('button', { name: '/login' }).click() |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/login' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['No further action', '/dashboard']) |
| }) |
|
|
| await test.step('select a different entry', async () => { |
| await firstColumn.getByRole('button', { name: '/another' }).click() |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/another-home' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/journey']) |
| }) |
|
|
| await test.step('select a different entry with no further steps', async () => { |
| await firstColumn.getByRole('button', { name: '/dashboard' }).click() |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/dashboard' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect(secondColumn).toHaveText(/No further steps found/) |
| }) |
|
|
| await test.step('deselect an entry', async () => { |
| await firstColumn.getByRole('button', { name: '/dashboard' }).click() |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/dashboard' }) |
| ).not.toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
| }) |
|
|
| await test.step('filter entries with match', async () => { |
| await firstColumn.getByPlaceholder('Search').fill('dash') |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/dashboard']) |
| }) |
|
|
| await test.step('filter entries with no match', async () => { |
| await firstColumn.getByPlaceholder('Search').fill('nosuchthing') |
|
|
| await expect(firstColumn).toHaveText(/No events found/) |
| }) |
|
|
| await test.step('filter returning more than one entry', async () => { |
| await firstColumn.getByPlaceholder('Search').fill('home') |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home', '/another-home']) |
| }) |
|
|
| await test.step('select an entry with filtering applied', async () => { |
| await firstColumn.getByRole('button', { name: '/home' }).click() |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login', 'No further action']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home', '/another-home']) |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/home' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
| }) |
|
|
| await test.step('select another entry with filtering still applied', async () => { |
| await firstColumn.getByRole('button', { name: '/another-home' }).click() |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/journey']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home', '/another-home']) |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/another-home' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
| }) |
|
|
| await test.step('deselect an entry with filtering still applied', async () => { |
| await firstColumn.getByRole('button', { name: '/another-home' }).click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home', '/login', '/another-home', '/dashboard', '/journey']) |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/another-home' }) |
| ).not.toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
| }) |
| }) |
|
|
| test('select entries in a 3-step journey', async ({ page, request }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: [ |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/dashboard', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/logout', |
| timestamp: { minutesAgo: 25 } |
| }, |
| { |
| user_id: 125, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 125, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 125, |
| name: 'pageview', |
| pathname: '/dashboard', |
| timestamp: { minutesAgo: 25 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/another-home', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/journey', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/somewhere', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 127, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 127, |
| name: 'pageview', |
| pathname: '/blog', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 128, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 40 } |
| } |
| ] |
| }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| await expect( |
| report.getByTestId('exploration-direction-forward') |
| ).toBeVisible() |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
| const secondColumn = report.getByTestId('exploration-column-1') |
| const thirdColumn = report.getByTestId('exploration-column-2') |
| const fourthColumn = report.getByTestId('exploration-column-3') |
|
|
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
|
|
| await firstColumn.getByRole('button', { name: '/home' }).click() |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login', 'No further action', '/blog']) |
|
|
| await test.step('filter for "No further action" in the second step', async () => { |
| await secondColumn.getByPlaceholder('Search').fill('no further') |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['No further action']) |
| }) |
|
|
| await secondColumn.getByPlaceholder('Search').fill('') |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login', 'No further action', '/blog']) |
|
|
| await test.step('select an entry in the 2nd step', async () => { |
| await secondColumn.getByRole('button', { name: '/login' }).click() |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/dashboard', '/logout']) |
|
|
| await expect( |
| secondColumn.getByRole('button', { name: '/login' }) |
| ).toHaveAttribute('data-exploration-step', '1') |
| }) |
|
|
| await test.step('select a different entry in the 2nd step', async () => { |
| await secondColumn.getByRole('button', { name: '/blog' }).click() |
|
|
| await expect(thirdColumn).toHaveText(/No further steps found/) |
|
|
| await expect( |
| secondColumn.getByRole('button', { name: '/blog' }) |
| ).toHaveAttribute('data-exploration-step', '1') |
| }) |
|
|
| await test.step('deselect an entry in the 2nd step', async () => { |
| await secondColumn.getByRole('button', { name: '/blog' }).click() |
|
|
| await expect(thirdColumn).toHaveText(/Select an event to continue/) |
|
|
| await expect( |
| secondColumn.getByRole('button', { name: '/blog' }) |
| ).not.toHaveAttribute('data-exploration-step', '1') |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login', 'No further action', '/blog']) |
| }) |
|
|
| await test.step('select a different entry in the 1st step', async () => { |
| await firstColumn.getByRole('button', { name: '/another-home' }).click() |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/another-home' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/journey']) |
|
|
| await expect(thirdColumn).toHaveText(/Select an event to continue/) |
| }) |
|
|
| await test.step('select an entry in the 2nd step and 3rd step', async () => { |
| await secondColumn.getByRole('button', { name: '/journey' }).click() |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/somewhere']) |
|
|
| await thirdColumn.getByRole('button', { name: '/somewhere' }).click() |
|
|
| await expect(fourthColumn).toHaveText(/No further steps found/) |
| }) |
|
|
| await test.step('deselect an entry at the 1st step', async () => { |
| await firstColumn.getByRole('button', { name: '/another-home' }).click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText([ |
| '/home', |
| '/login', |
| '/dashboard', |
| '/another-home', |
| '/blog', |
| '/journey', |
| '/logout', |
| '/somewhere' |
| ]) |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/another-home' }) |
| ).not.toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
|
|
| await expect(thirdColumn).toHaveText(/Select an event to continue/) |
|
|
| await expect(fourthColumn).toBeHidden() |
| }) |
| }) |
|
|
| test('explore journey hitting 20 step limit', async ({ page, request }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| const events = [...Array(21).keys()].map((i) => { |
| return { |
| user_id: 123, |
| name: 'pageview', |
| pathname: `/page${i}`, |
| timestamp: { minutesAgo: 100 - i } |
| } |
| }) |
|
|
| await populateStats({ request, domain, events }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| await expect( |
| report.getByTestId('exploration-direction-forward') |
| ).toBeVisible() |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
| const lastColumn = report.getByTestId('exploration-column-20') |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText([ |
| '/page0', |
| '/page1', |
| '/page10', |
| '/page11', |
| '/page12', |
| '/page13', |
| '/page14', |
| '/page15', |
| '/page16', |
| '/page17' |
| ]) |
|
|
| await (async () => { |
| for await (const i of [...Array(20).keys()]) { |
| const column = report.getByTestId(`exploration-column-${i}`) |
|
|
| await column.getByRole('button', { name: `/page${i}` }).click() |
|
|
| await expect( |
| column.getByRole('button', { name: `/page${i}` }) |
| ).toHaveAttribute('data-exploration-step', `${i}`) |
| } |
| })() |
|
|
| await expect(lastColumn).toHaveText( |
| /You've reached the maximum journey depth/ |
| ) |
| }) |
|
|
| test('explore from end point', async ({ page, request }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: [ |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 123, |
| name: 'pageview', |
| pathname: '/dashboard', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 124, |
| name: 'pageview', |
| pathname: '/logout', |
| timestamp: { minutesAgo: 25 } |
| }, |
| { |
| user_id: 125, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 125, |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 125, |
| name: 'pageview', |
| pathname: '/dashboard', |
| timestamp: { minutesAgo: 25 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/another-home', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/journey', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 126, |
| name: 'pageview', |
| pathname: '/somewhere', |
| timestamp: { minutesAgo: 30 } |
| }, |
| { |
| user_id: 127, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 40 } |
| }, |
| { |
| user_id: 127, |
| name: 'pageview', |
| pathname: '/blog', |
| timestamp: { minutesAgo: 35 } |
| }, |
| { |
| user_id: 128, |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 40 } |
| } |
| ] |
| }) |
|
|
| const firstStepSuggestions = [ |
| '/home', |
| '/login', |
| '/dashboard', |
| '/another-home', |
| '/blog', |
| '/journey', |
| '/logout', |
| '/somewhere' |
| ] |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
| const secondColumn = report.getByTestId('exploration-column-1') |
| const thirdColumn = report.getByTestId('exploration-column-2') |
| const fourthColumn = report.getByTestId('exploration-column-3') |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(firstStepSuggestions) |
|
|
| await test.step('switch to end point, back to start point and to end point again', async () => { |
| await report.getByTestId('exploration-direction-forward').click() |
| await report.getByTestId('exploration-direction-backward').click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(firstStepSuggestions) |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/home' }) |
| ).not.toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(firstStepSuggestions) |
|
|
| await expect(secondColumn).toHaveText(/1 step before/) |
| await expect(secondColumn).toHaveText(/Select an end point to continue/) |
| await expect(thirdColumn).toHaveText(/2 steps before/) |
| await expect(thirdColumn).toHaveText(/Select an event to continue/) |
| await expect(fourthColumn).toBeHidden() |
|
|
| await report.getByTestId('exploration-direction-backward').click() |
| await report.getByTestId('exploration-direction-forward').click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(firstStepSuggestions) |
|
|
| await expect(secondColumn).toHaveText(/1 step after/) |
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
| await expect(thirdColumn).toHaveText(/2 steps after/) |
| await expect(thirdColumn).toHaveText(/Select an event to continue/) |
| await expect(fourthColumn).toBeHidden() |
| }) |
|
|
| await test.step('explore a 3-step journey from an end point', async () => { |
| await report.getByTestId('exploration-direction-forward').click() |
| await report.getByTestId('exploration-direction-backward').click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(firstStepSuggestions) |
|
|
| await firstColumn.getByRole('button', { name: '/dashboard' }).click() |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/dashboard' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login']) |
|
|
| await expect(thirdColumn).toHaveText(/Select an event to continue/) |
|
|
| await secondColumn.getByRole('button', { name: '/login' }).click() |
|
|
| await expect( |
| secondColumn.getByRole('button', { name: '/login' }) |
| ).toHaveAttribute('data-exploration-step', '1') |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home']) |
|
|
| await thirdColumn.getByRole('button', { name: '/home' }).click() |
|
|
| await expect( |
| thirdColumn.getByRole('button', { name: '/home' }) |
| ).toHaveAttribute('data-exploration-step', '2') |
|
|
| await expect(fourthColumn).toHaveText(/No further steps found/) |
| }) |
|
|
| await test.step('switch back to starting point', async () => { |
| await report.getByTestId('exploration-direction-backward').click() |
| await report.getByTestId('exploration-direction-forward').click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(firstStepSuggestions) |
| }) |
| }) |
|
|
| test('change filters during a 3-step journey', async ({ page, request }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: [ |
| { |
| user_id: 123, |
| browser: 'Firefox', |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 20 } |
| }, |
| { |
| user_id: 123, |
| browser: 'Firefox', |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 19 } |
| }, |
| { |
| user_id: 123, |
| browser: 'Chrome', |
| name: 'pageview', |
| pathname: '/dashboard', |
| timestamp: { minutesAgo: 18 } |
| }, |
| { |
| user_id: 124, |
| browser: 'Chrome', |
| name: 'pageview', |
| pathname: '/home', |
| timestamp: { minutesAgo: 20 } |
| }, |
| { |
| user_id: 124, |
| browser: 'Chrome', |
| name: 'pageview', |
| pathname: '/login', |
| timestamp: { minutesAgo: 19 } |
| }, |
| { |
| user_id: 125, |
| browser: 'Safari', |
| name: 'pageview', |
| pathname: '/other', |
| timestamp: { minutesAgo: 20 } |
| }, |
| { |
| user_id: 125, |
| browser: 'Safari', |
| name: 'pageview', |
| pathname: '/journey', |
| timestamp: { minutesAgo: 19 } |
| } |
| ] |
| }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| await expect( |
| report.getByTestId('exploration-direction-forward') |
| ).toBeVisible() |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
| const secondColumn = report.getByTestId('exploration-column-1') |
| const thirdColumn = report.getByTestId('exploration-column-2') |
| const fourthColumn = report.getByTestId('exploration-column-3') |
|
|
| await firstColumn.getByRole('button', { name: '/home' }).click() |
|
|
| await expect( |
| firstColumn.getByRole('button', { name: '/home' }) |
| ).toHaveAttribute('data-exploration-step', '0') |
|
|
| await secondColumn.getByRole('button', { name: '/login' }).click() |
|
|
| await expect( |
| secondColumn.getByRole('button', { name: '/login' }) |
| ).toHaveAttribute('data-exploration-step', '1') |
|
|
| await thirdColumn.getByRole('button', { name: '/dashboard' }).click() |
|
|
| await expect( |
| thirdColumn.getByRole('button', { name: '/dashboard' }) |
| ).toHaveAttribute('data-exploration-step', '2') |
|
|
| await expect(fourthColumn).toHaveText(/No further steps found/) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home', '/login', '/dashboard', '/journey', '/other']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['2', '2', '1', '1', '1']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['2']) |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['No further action', '/dashboard']) |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1', '1']) |
|
|
| await test.step('filtering with journey matches in all 3 steps', async () => { |
| |
| await page |
| .getByTestId('report-devices') |
| .getByRole('link', { name: 'Firefox' }) |
| .click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1']) |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/dashboard']) |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1']) |
|
|
| await expect(fourthColumn).toHaveText(/No further steps found/) |
|
|
| |
| page |
| .getByRole('button', { name: 'Remove filter: Browser is Firefox' }) |
| .click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['2']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['2']) |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/dashboard']) |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1']) |
|
|
| await expect(fourthColumn).toHaveText(/No further steps found/) |
| }) |
|
|
| await test.step('filtering with journey matches for 2 first steps', async () => { |
| |
| await page |
| .getByTestId('report-devices') |
| .getByRole('link', { name: 'Chrome' }) |
| .click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1']) |
|
|
| await expect(thirdColumn).toHaveText(/No further steps found/) |
|
|
| await expect(fourthColumn).toBeHidden() |
|
|
| |
| page |
| .getByRole('button', { name: 'Remove filter: Browser is Chrome' }) |
| .click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['2']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/login']) |
|
|
| await expect( |
| secondColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['2']) |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['No further action', '/dashboard']) |
|
|
| await expect( |
| thirdColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1', '1']) |
|
|
| await expect( |
| thirdColumn.getByRole('button', { name: '/dashboard' }) |
| ).not.toHaveAttribute('data-exploration-step', '2') |
|
|
| await expect(fourthColumn).toBeHidden() |
| }) |
|
|
| |
| await thirdColumn.getByRole('button', { name: '/dashboard' }).click() |
|
|
| await expect( |
| thirdColumn.getByRole('button', { name: '/dashboard' }) |
| ).toHaveAttribute('data-exploration-step', '2') |
|
|
| await test.step('filtering with journey with no matches for any of the steps', async () => { |
| |
| await page |
| .getByTestId('report-devices') |
| .getByRole('link', { name: 'Safari' }) |
| .click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/journey', '/other']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['1', '1']) |
|
|
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
|
|
| await expect(thirdColumn).toHaveText(/Select an event to continue/) |
|
|
| await expect(fourthColumn).toBeHidden() |
|
|
| |
| page |
| .getByRole('button', { name: 'Remove filter: Browser is Safari' }) |
| .click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(['/home', '/login', '/dashboard', '/journey', '/other']) |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-value') |
| ).toHaveText(['2', '2', '1', '1', '1']) |
|
|
| await expect(secondColumn).toHaveText(/Select a starting point to continue/) |
|
|
| await expect(thirdColumn).toHaveText(/Select an event to continue/) |
|
|
| await expect(fourthColumn).toBeHidden() |
| }) |
| }) |
|
|
| test('render various types of entries', async ({ page, request }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: [ |
| { |
| name: 'pageview', |
| pathname: '/home' |
| }, |
| { |
| name: 'pageview', |
| pathname: '/login' |
| }, |
| { |
| name: 'checkout' |
| }, |
| { |
| name: 'create_site' |
| }, |
| { |
| name: 'pageview', |
| pathname: '/blog/first-post' |
| }, |
| { |
| name: 'pageview', |
| pathname: '/blog/second-post' |
| } |
| ] |
| }) |
|
|
| await addGoal({ |
| request, |
| domain, |
| params: { event_name: 'create_site', display_name: 'Create a site' } |
| }) |
|
|
| await addGoal({ request, domain, params: { page_path: '/login' } }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| await expect( |
| report.getByTestId('exploration-direction-forward') |
| ).toBeVisible() |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText([ |
| '/blog', |
| 'checkout', |
| 'Create a site', |
| '/blog/first-post', |
| '/blog/second-post', |
| '/home', |
| 'Visit /login' |
| ]) |
|
|
| await firstColumn.getByTestId('exploration-row').nth(0).locator('svg').hover() |
| await expect(page.getByRole('tooltip')).toHaveText( |
| /Grouped pages: 2 pages with this prefix/ |
| ) |
|
|
| await firstColumn.getByTestId('exploration-row').nth(2).locator('svg').hover() |
| await expect(page.getByRole('tooltip')).toHaveText(/Goal/) |
|
|
| await firstColumn.getByTestId('exploration-row').nth(6).locator('svg').hover() |
| await expect(page.getByRole('tooltip')).toHaveText(/Goal/) |
| }) |
|
|
| test('load more suggestions', async ({ page, request }) => { |
| const report = getReport(page) |
| const explorationTabButton = getExplorationTabButton(report) |
| const { domain } = await setupSite({ page, request }) |
|
|
| const events1 = [...Array(25).keys()].map((i) => { |
| return { |
| name: 'pageview', |
| pathname: `/pageone${String(i).padStart(2, '0')}` |
| } |
| }) |
|
|
| const events2 = [...Array(25).keys()].map((i) => { |
| return { |
| name: 'pageview', |
| pathname: `/pagetwo${String(i).padStart(2, '0')}` |
| } |
| }) |
|
|
| await populateStats({ |
| request, |
| domain, |
| events: events1.concat(events2) |
| }) |
|
|
| await page.goto('/' + domain, { waitUntil: 'commit' }) |
|
|
| await explorationTabButton.click() |
| await report.getByTestId('report-end').scrollIntoViewIfNeeded() |
|
|
| await expect(report.getByTestId('exploration-title')).toHaveText( |
| 'Explore user journeys' |
| ) |
|
|
| await expect( |
| report.getByTestId('exploration-direction-forward') |
| ).toBeVisible() |
|
|
| const firstColumn = report.getByTestId('exploration-column-0') |
|
|
| const columnRow = (i: number) => |
| firstColumn.getByTestId('exploration-row').nth(i) |
|
|
| const firstPage = [ |
| '/pageone00', |
| '/pageone01', |
| '/pageone02', |
| '/pageone03', |
| '/pageone04', |
| '/pageone05', |
| '/pageone06', |
| '/pageone07', |
| '/pageone08', |
| '/pageone09' |
| ] |
|
|
| const secondPage = [ |
| '/pageone10', |
| '/pageone11', |
| '/pageone12', |
| '/pageone13', |
| '/pageone14', |
| '/pageone15', |
| '/pageone16', |
| '/pageone17', |
| '/pageone18', |
| '/pageone19' |
| ] |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(firstPage) |
|
|
| await expect(columnRow(10)).toHaveText(/Show 10 more/) |
| await columnRow(10).click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(firstPage.concat(secondPage)) |
|
|
| await expect(columnRow(20)).toHaveText(/Show 10 more/) |
|
|
| await test.step('reset state when suggestions change', async () => { |
| await firstColumn.getByPlaceholder('Search').fill('pagetwo') |
|
|
| const newFirstPage = [ |
| '/pagetwo00', |
| '/pagetwo01', |
| '/pagetwo02', |
| '/pagetwo03', |
| '/pagetwo04', |
| '/pagetwo05', |
| '/pagetwo06', |
| '/pagetwo07', |
| '/pagetwo08', |
| '/pagetwo09' |
| ] |
|
|
| const newSecondPage = [ |
| '/pagetwo10', |
| '/pagetwo11', |
| '/pagetwo12', |
| '/pagetwo13', |
| '/pagetwo14', |
| '/pagetwo15', |
| '/pagetwo16', |
| '/pagetwo17', |
| '/pagetwo18', |
| '/pagetwo19' |
| ] |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(newFirstPage) |
|
|
| await expect(columnRow(10)).toHaveText(/Show 10 more/) |
| await columnRow(10).click() |
|
|
| await expect( |
| firstColumn.getByTestId('exploration-row').getByTestId('metric-label') |
| ).toHaveText(newFirstPage.concat(newSecondPage)) |
|
|
| await expect(columnRow(20)).toHaveText(/Show 5 more/) |
| }) |
| }) |
|
|