diff --git a/.github/workflows/ci-e2e.yml b/.github/workflows/ci-e2e.yml index f00214da5d20a..5538f237dfca1 100644 --- a/.github/workflows/ci-e2e.yml +++ b/.github/workflows/ci-e2e.yml @@ -65,7 +65,7 @@ jobs: - name: Group spec files into chunks of three id: chunk - run: echo "chunks=$(ls cypress/e2e/* | jq --slurp --raw-input -c 'split("\n")[:-1] | _nwise(3) | join("\n")' | jq --slurp -c .)" >> $GITHUB_OUTPUT + run: echo "chunks=$(ls cypress/e2e/* | jq --slurp --raw-input -c 'split("\n")[:-1] | _nwise(2) | join("\n")' | jq --slurp -c .)" >> $GITHUB_OUTPUT container: name: Build and cache container image diff --git a/cypress/e2e/auth-password-reset.cy.ts b/cypress/e2e/auth-password-reset.cy.ts new file mode 100644 index 0000000000000..8669141a52c4f --- /dev/null +++ b/cypress/e2e/auth-password-reset.cy.ts @@ -0,0 +1,54 @@ +describe('Password Reset', () => { + beforeEach(() => { + cy.get('[data-attr=top-menu-toggle]').click() + cy.get('[data-attr=top-menu-item-logout]').click() + cy.location('pathname').should('eq', '/login') + }) + + it('Can request password reset', () => { + cy.get('[data-attr=login-email]').type('fake@posthog.com').should('have.value', 'fake@posthog.com').blur() + cy.get('[data-attr=forgot-password]', { timeout: 5000 }).should('be.visible') // Wait for login precheck (note blur above) + cy.get('[data-attr="forgot-password"]').click() + cy.location('pathname').should('eq', '/reset') + cy.get('[data-attr="reset-email"]').type('test@posthog.com') + cy.get('button[type=submit]').click() + cy.get('div').should('contain', 'Request received successfully!') + cy.get('b').should('contain', 'test@posthog.com') + }) + + it('Cannot reset with invalid token', () => { + cy.visit('/reset/user_id/token') + cy.get('div').should('contain', 'The provided link is invalid or has expired. ') + }) + + it('Shows validation error if passwords do not match', () => { + cy.visit('/reset/e2e_test_user/e2e_test_token') + cy.get('[data-attr="password"]').type('12345678') + cy.get('.ant-progress-bg').should('be.visible') + cy.get('[data-attr="password-confirm"]').type('1234567A') + cy.get('button[type=submit]').click() + cy.get('.text-danger').should('contain', 'Passwords do not match') + cy.location('pathname').should('eq', '/reset/e2e_test_user/e2e_test_token') // not going anywhere + }) + + it('Shows validation error if password is too short', () => { + cy.visit('/reset/e2e_test_user/e2e_test_token') + cy.get('[data-attr="password"]').type('123') + cy.get('[data-attr="password-confirm"]').type('123') + cy.get('button[type=submit]').click() + cy.get('.text-danger').should('be.visible') + cy.get('.text-danger').should('contain', 'must be at least 8 characters') + cy.location('pathname').should('eq', '/reset/e2e_test_user/e2e_test_token') // not going anywhere + }) + + it('Can reset password with valid token', () => { + cy.visit('/reset/e2e_test_user/e2e_test_token') + cy.get('[data-attr="password"]').type('NEW123456789') + cy.get('[data-attr="password-confirm"]').type('NEW123456789') + cy.get('button[type=submit]').click() + cy.get('.Toastify__toast--success').should('be.visible') + + // assert the user was redirected; can't test actual redirection to /insights because the test handler doesn't actually log in the user + cy.location('pathname').should('not.contain', '/reset/e2e_test_user/e2e_test_token') + }) +}) diff --git a/cypress/e2e/auth.cy.ts b/cypress/e2e/auth.cy.ts index dae569410a12e..e837f83a5b9f7 100644 --- a/cypress/e2e/auth.cy.ts +++ b/cypress/e2e/auth.cy.ts @@ -87,58 +87,3 @@ describe('Auth', () => { cy.location('pathname').should('eq', urls.projectHomepage()) }) }) - -describe('Password Reset', () => { - beforeEach(() => { - cy.get('[data-attr=top-menu-toggle]').click() - cy.get('[data-attr=top-menu-item-logout]').click() - cy.location('pathname').should('eq', '/login') - }) - - it('Can request password reset', () => { - cy.get('[data-attr=login-email]').type('fake@posthog.com').should('have.value', 'fake@posthog.com').blur() - cy.get('[data-attr=forgot-password]', { timeout: 5000 }).should('be.visible') // Wait for login precheck (note blur above) - cy.get('[data-attr="forgot-password"]').click() - cy.location('pathname').should('eq', '/reset') - cy.get('[data-attr="reset-email"]').type('test@posthog.com') - cy.get('button[type=submit]').click() - cy.get('div').should('contain', 'Request received successfully!') - cy.get('b').should('contain', 'test@posthog.com') - }) - - it('Cannot reset with invalid token', () => { - cy.visit('/reset/user_id/token') - cy.get('div').should('contain', 'The provided link is invalid or has expired. ') - }) - - it('Shows validation error if passwords do not match', () => { - cy.visit('/reset/e2e_test_user/e2e_test_token') - cy.get('[data-attr="password"]').type('12345678') - cy.get('.ant-progress-bg').should('be.visible') - cy.get('[data-attr="password-confirm"]').type('1234567A') - cy.get('button[type=submit]').click() - cy.get('.text-danger').should('contain', 'Passwords do not match') - cy.location('pathname').should('eq', '/reset/e2e_test_user/e2e_test_token') // not going anywhere - }) - - it('Shows validation error if password is too short', () => { - cy.visit('/reset/e2e_test_user/e2e_test_token') - cy.get('[data-attr="password"]').type('123') - cy.get('[data-attr="password-confirm"]').type('123') - cy.get('button[type=submit]').click() - cy.get('.text-danger').should('be.visible') - cy.get('.text-danger').should('contain', 'must be at least 8 characters') - cy.location('pathname').should('eq', '/reset/e2e_test_user/e2e_test_token') // not going anywhere - }) - - it('Can reset password with valid token', () => { - cy.visit('/reset/e2e_test_user/e2e_test_token') - cy.get('[data-attr="password"]').type('NEW123456789') - cy.get('[data-attr="password-confirm"]').type('NEW123456789') - cy.get('button[type=submit]').click() - cy.get('.Toastify__toast--success').should('be.visible') - - // assert the user was redirected; can't test actual redirection to /insights because the test handler doesn't actually log in the user - cy.location('pathname').should('not.contain', '/reset/e2e_test_user/e2e_test_token') - }) -}) diff --git a/cypress/e2e/dashboard-deletion.ts b/cypress/e2e/dashboard-deletion.ts new file mode 100644 index 0000000000000..42f2194b9f0c2 --- /dev/null +++ b/cypress/e2e/dashboard-deletion.ts @@ -0,0 +1,61 @@ +import { urls } from 'scenes/urls' +import { randomString } from '../support/random' +import { dashboard, dashboards, insight, savedInsights } from '../productAnalytics' + +describe('deleting dashboards', () => { + it('can delete dashboard without deleting the insights', () => { + cy.visit(urls.savedInsights()) // get insights list into turbo mode + cy.clickNavMenu('dashboards') + + const dashboardName = randomString('dashboard-') + const insightName = randomString('insight-') + + dashboards.createAndGoToEmptyDashboard(dashboardName) + dashboard.addInsightToEmptyDashboard(insightName) + + cy.get('[data-attr="dashboard-three-dots-options-menu"]').click() + cy.get('button').contains('Delete dashboard').click() + cy.get('[data-attr="dashboard-delete-submit"]').click() + + savedInsights.checkInsightIsInListView(insightName) + }) + + // TODO: this test works locally, just not in CI + it.skip('can delete dashboard and delete the insights', () => { + cy.visit(urls.savedInsights()) // get insights list into turbo mode + cy.clickNavMenu('dashboards') + + const dashboardName = randomString('dashboard-') + const dashboardToKeepName = randomString('dashboard-to-keep') + const insightName = randomString('insight-') + const insightToKeepName = randomString('insight-to-keep-') + + dashboards.createAndGoToEmptyDashboard(dashboardName) + dashboard.addInsightToEmptyDashboard(insightName) + + cy.clickNavMenu('dashboards') + + dashboards.createAndGoToEmptyDashboard(dashboardToKeepName) + dashboard.addInsightToEmptyDashboard(insightToKeepName) + + cy.visit(urls.savedInsights()) + cy.wait('@loadInsightList').then(() => { + cy.get('.saved-insights tr a').should('be.visible') + + // load the named insight + cy.contains('.saved-insights tr', insightToKeepName).within(() => { + cy.get('.row-name a').click() + }) + + insight.addInsightToDashboard(dashboardName, { visitAfterAdding: true }) + + cy.get('[data-attr="dashboard-three-dots-options-menu"]').click() + cy.get('button').contains('Delete dashboard').click() + cy.contains('span.LemonCheckbox', "Delete this dashboard's insights").click() + cy.get('[data-attr="dashboard-delete-submit"]').click() + + savedInsights.checkInsightIsInListView(insightToKeepName) + savedInsights.checkInsightIsNotInListView(insightName) + }) + }) +}) diff --git a/cypress/e2e/dashboard-duplication.ts b/cypress/e2e/dashboard-duplication.ts new file mode 100644 index 0000000000000..0be744d46980f --- /dev/null +++ b/cypress/e2e/dashboard-duplication.ts @@ -0,0 +1,98 @@ +import { randomString } from '../support/random' +import { urls } from 'scenes/urls' +import { dashboard, dashboards, duplicateDashboardFromMenu, savedInsights } from '../productAnalytics' + +describe('duplicating dashboards', () => { + let dashboardName, insightName, expectedCopiedDashboardName, expectedCopiedInsightName + + beforeEach(() => { + dashboardName = randomString('dashboard-') + expectedCopiedDashboardName = `${dashboardName} (Copy)` + + insightName = randomString('insight-') + expectedCopiedInsightName = `${insightName} (Copy)` + + cy.visit(urls.savedInsights()) // get insights list into turbo mode + cy.clickNavMenu('dashboards') + + dashboards.createAndGoToEmptyDashboard(dashboardName) + dashboard.addInsightToEmptyDashboard(insightName) + + cy.contains('h4', insightName).click() // get insight into turbo mode + }) + + describe('from the dashboard list', () => { + it('can duplicate a dashboard without duplicating insights', () => { + cy.clickNavMenu('dashboards') + cy.get('[placeholder="Search for dashboards"]').type(dashboardName) + + cy.contains('[data-attr="dashboards-table"] tr', dashboardName).within(() => { + cy.get('[data-attr="more-button"]').click() + }) + duplicateDashboardFromMenu() + cy.get('h1.page-title').should('have.text', expectedCopiedDashboardName) + + cy.wait('@createDashboard').then(() => { + cy.get('.CardMeta h4').should('have.text', insightName).should('not.have.text', '(Copy)') + cy.contains('h4', insightName).click() + // this works when actually using the site, but not in Cypress + // cy.get('[data-attr="save-to-dashboard-button"] .LemonBadge').should('have.text', '2') + }) + }) + + it('can duplicate a dashboard and duplicate insights', () => { + cy.clickNavMenu('dashboards') + cy.get('[placeholder="Search for dashboards"]').type(dashboardName) + + cy.contains('[data-attr="dashboards-table"] tr', dashboardName).within(() => { + cy.get('[data-attr="more-button"]').click() + }) + duplicateDashboardFromMenu(true) + cy.get('h1.page-title').should('have.text', expectedCopiedDashboardName) + + cy.wait('@createDashboard').then(() => { + cy.contains('h4', expectedCopiedInsightName).click() + cy.get('[data-attr="save-to-dashboard-button"] .LemonBadge').should('have.text', '1') + }) + + savedInsights.checkInsightIsInListView(insightName) + savedInsights.checkInsightIsInListView(expectedCopiedInsightName) + }) + }) + + describe('from the dashboard', () => { + it('can duplicate a dashboard without duplicating insights', () => { + cy.clickNavMenu('dashboards') + dashboards.visitDashboard(dashboardName) + + cy.get('[data-attr="dashboard-three-dots-options-menu"]').click() + duplicateDashboardFromMenu() + cy.get('h1.page-title').should('have.text', expectedCopiedDashboardName) + + cy.wait('@createDashboard').then(() => { + cy.get('.CardMeta h4').should('have.text', insightName).should('not.have.text', '(Copy)') + cy.contains('h4', insightName).click() + // this works when actually using the site, but not in Cypress + // cy.get('[data-attr="save-to-dashboard-button"] .LemonBadge').should('have.text', '2') + }) + savedInsights.checkInsightIsInListView(insightName) + savedInsights.checkInsightIsNotInListView(expectedCopiedInsightName) + }) + it('can duplicate a dashboard and duplicate insights', () => { + cy.clickNavMenu('dashboards') + dashboards.visitDashboard(dashboardName) + + cy.get('[data-attr="dashboard-three-dots-options-menu"]').click() + duplicateDashboardFromMenu(true) + cy.get('h1.page-title').should('have.text', expectedCopiedDashboardName) + + cy.wait('@createDashboard').then(() => { + cy.contains('h4', expectedCopiedInsightName).click() + cy.get('[data-attr="save-to-dashboard-button"] .LemonBadge').should('have.text', '1') + }) + + savedInsights.checkInsightIsInListView(insightName) + savedInsights.checkInsightIsInListView(expectedCopiedInsightName) + }) + }) +}) diff --git a/cypress/e2e/dashboard.cy.ts b/cypress/e2e/dashboard.cy.ts index fcf0b6f066db2..b40466727c716 100644 --- a/cypress/e2e/dashboard.cy.ts +++ b/cypress/e2e/dashboard.cy.ts @@ -1,6 +1,5 @@ -import { urls } from 'scenes/urls' import { randomString } from '../support/random' -import { insight, savedInsights, dashboards, dashboard, duplicateDashboardFromMenu } from '../productAnalytics' +import { insight, dashboards, dashboard } from '../productAnalytics' describe('Dashboard', () => { beforeEach(() => { @@ -211,157 +210,4 @@ describe('Dashboard', () => { cy.wait(200) cy.get('.page-title').contains(dashboardName).should('exist') }) - - describe('duplicating dashboards', () => { - let dashboardName, insightName, expectedCopiedDashboardName, expectedCopiedInsightName - - beforeEach(() => { - dashboardName = randomString('dashboard-') - expectedCopiedDashboardName = `${dashboardName} (Copy)` - - insightName = randomString('insight-') - expectedCopiedInsightName = `${insightName} (Copy)` - - cy.visit(urls.savedInsights()) // get insights list into turbo mode - cy.clickNavMenu('dashboards') - - dashboards.createAndGoToEmptyDashboard(dashboardName) - dashboard.addInsightToEmptyDashboard(insightName) - - cy.contains('h4', insightName).click() // get insight into turbo mode - }) - - describe('from the dashboard list', () => { - it('can duplicate a dashboard without duplicating insights', () => { - cy.clickNavMenu('dashboards') - cy.get('[placeholder="Search for dashboards"]').type(dashboardName) - - cy.contains('[data-attr="dashboards-table"] tr', dashboardName).within(() => { - cy.get('[data-attr="more-button"]').click() - }) - duplicateDashboardFromMenu() - cy.get('h1.page-title').should('have.text', expectedCopiedDashboardName) - - cy.wait('@createDashboard').then(() => { - cy.get('.CardMeta h4').should('have.text', insightName).should('not.have.text', '(Copy)') - cy.contains('h4', insightName).click() - // this works when actually using the site, but not in Cypress - // cy.get('[data-attr="save-to-dashboard-button"] .LemonBadge').should('have.text', '2') - }) - }) - - it('can duplicate a dashboard and duplicate insights', () => { - cy.clickNavMenu('dashboards') - cy.get('[placeholder="Search for dashboards"]').type(dashboardName) - - cy.contains('[data-attr="dashboards-table"] tr', dashboardName).within(() => { - cy.get('[data-attr="more-button"]').click() - }) - duplicateDashboardFromMenu(true) - cy.get('h1.page-title').should('have.text', expectedCopiedDashboardName) - - cy.wait('@createDashboard').then(() => { - cy.contains('h4', expectedCopiedInsightName).click() - cy.get('[data-attr="save-to-dashboard-button"] .LemonBadge').should('have.text', '1') - }) - - savedInsights.checkInsightIsInListView(insightName) - savedInsights.checkInsightIsInListView(expectedCopiedInsightName) - }) - }) - - describe('from the dashboard', () => { - it('can duplicate a dashboard without duplicating insights', () => { - cy.clickNavMenu('dashboards') - dashboards.visitDashboard(dashboardName) - - cy.get('[data-attr="dashboard-three-dots-options-menu"]').click() - duplicateDashboardFromMenu() - cy.get('h1.page-title').should('have.text', expectedCopiedDashboardName) - - cy.wait('@createDashboard').then(() => { - cy.get('.CardMeta h4').should('have.text', insightName).should('not.have.text', '(Copy)') - cy.contains('h4', insightName).click() - // this works when actually using the site, but not in Cypress - // cy.get('[data-attr="save-to-dashboard-button"] .LemonBadge').should('have.text', '2') - }) - savedInsights.checkInsightIsInListView(insightName) - savedInsights.checkInsightIsNotInListView(expectedCopiedInsightName) - }) - it('can duplicate a dashboard and duplicate insights', () => { - cy.clickNavMenu('dashboards') - dashboards.visitDashboard(dashboardName) - - cy.get('[data-attr="dashboard-three-dots-options-menu"]').click() - duplicateDashboardFromMenu(true) - cy.get('h1.page-title').should('have.text', expectedCopiedDashboardName) - - cy.wait('@createDashboard').then(() => { - cy.contains('h4', expectedCopiedInsightName).click() - cy.get('[data-attr="save-to-dashboard-button"] .LemonBadge').should('have.text', '1') - }) - - savedInsights.checkInsightIsInListView(insightName) - savedInsights.checkInsightIsInListView(expectedCopiedInsightName) - }) - }) - }) - - describe('deleting dashboards', () => { - it('can delete dashboard without deleting the insights', () => { - cy.visit(urls.savedInsights()) // get insights list into turbo mode - cy.clickNavMenu('dashboards') - - const dashboardName = randomString('dashboard-') - const insightName = randomString('insight-') - - dashboards.createAndGoToEmptyDashboard(dashboardName) - dashboard.addInsightToEmptyDashboard(insightName) - - cy.get('[data-attr="dashboard-three-dots-options-menu"]').click() - cy.get('button').contains('Delete dashboard').click() - cy.get('[data-attr="dashboard-delete-submit"]').click() - - savedInsights.checkInsightIsInListView(insightName) - }) - - // TODO: this test works locally, just not in CI - it.skip('can delete dashboard and delete the insights', () => { - cy.visit(urls.savedInsights()) // get insights list into turbo mode - cy.clickNavMenu('dashboards') - - const dashboardName = randomString('dashboard-') - const dashboardToKeepName = randomString('dashboard-to-keep') - const insightName = randomString('insight-') - const insightToKeepName = randomString('insight-to-keep-') - - dashboards.createAndGoToEmptyDashboard(dashboardName) - dashboard.addInsightToEmptyDashboard(insightName) - - cy.clickNavMenu('dashboards') - - dashboards.createAndGoToEmptyDashboard(dashboardToKeepName) - dashboard.addInsightToEmptyDashboard(insightToKeepName) - - cy.visit(urls.savedInsights()) - cy.wait('@loadInsightList').then(() => { - cy.get('.saved-insights tr a').should('be.visible') - - // load the named insight - cy.contains('.saved-insights tr', insightToKeepName).within(() => { - cy.get('.row-name a').click() - }) - - insight.addInsightToDashboard(dashboardName, { visitAfterAdding: true }) - - cy.get('[data-attr="dashboard-three-dots-options-menu"]').click() - cy.get('button').contains('Delete dashboard').click() - cy.contains('span.LemonCheckbox', "Delete this dashboard's insights").click() - cy.get('[data-attr="dashboard-delete-submit"]').click() - - savedInsights.checkInsightIsInListView(insightToKeepName) - savedInsights.checkInsightIsNotInListView(insightName) - }) - }) - }) }) diff --git a/cypress/e2e/insights-date-picker.ts b/cypress/e2e/insights-date-picker.ts new file mode 100644 index 0000000000000..cdf3bb0beca5f --- /dev/null +++ b/cypress/e2e/insights-date-picker.ts @@ -0,0 +1,18 @@ +describe('insights date picker', () => { + it('Can set the date filter and show the right grouping interval', () => { + cy.get('[data-attr=date-filter]').click() + cy.get('div').contains('Yesterday').should('exist').click() + cy.get('[data-attr=interval-filter]').should('contain', 'Hour') + }) + + it('Can set a custom rolling date range', () => { + cy.get('[data-attr=date-filter]').click() + cy.get('[data-attr=rolling-date-range-input]').type('{selectall}5{enter}') + cy.get('[data-attr=rolling-date-range-date-options-selector]').click() + cy.get('.RollingDateRangeFilter__popover > div').contains('days').should('exist').click() + cy.get('.RollingDateRangeFilter__label').should('contain', 'In the last').click() + + // Test that the button shows the correct formatted range + cy.get('[data-attr=date-filter]').get('span').contains('Last 5 days').should('exist') + }) +}) diff --git a/cypress/e2e/insights-navigation-open-directly.cy.ts b/cypress/e2e/insights-navigation-open-directly.cy.ts new file mode 100644 index 0000000000000..406445d9f6636 --- /dev/null +++ b/cypress/e2e/insights-navigation-open-directly.cy.ts @@ -0,0 +1,71 @@ +import { urls } from 'scenes/urls' +import { decideResponse } from '../fixtures/api/decide' +import { insight } from '../productAnalytics' + +const hogQLQuery = `select event, + count() + from events + group by event, + properties.$browser, + person.properties.email + order by count() desc + limit 2` + +// For tests related to trends please check trendsElements.js +describe('Insights', () => { + beforeEach(() => { + cy.intercept('https://app.posthog.com/decide/*', (req) => + req.reply( + decideResponse({ + hogql: true, + 'data-exploration-insights': true, + }) + ) + ) + + cy.visit(urls.insightNew()) + }) + + describe('navigation', () => { + describe('opening a new insight directly', () => { + it('can open a new trends insight', () => { + insight.newInsight('TRENDS') + cy.get('.trends-insights-container canvas').should('exist') + cy.get('tr').should('have.length.gte', 2) + }) + + it('can open a new funnels insight', () => { + insight.newInsight('FUNNELS') + cy.get('.funnels-empty-state__title').should('exist') + }) + + it.skip('can open a new retention insight', () => { + insight.newInsight('RETENTION') + cy.get('.RetentionContainer canvas').should('exist') + cy.get('.RetentionTable__Tab').should('have.length', 66) + }) + + it('can open a new paths insight', () => { + insight.newInsight('PATHS') + cy.get('.Paths g').should('have.length.gte', 5) // not a fixed value unfortunately + }) + + it('can open a new stickiness insight', () => { + insight.newInsight('STICKINESS') + cy.get('.trends-insights-container canvas').should('exist') + }) + + it('can open a new lifecycle insight', () => { + insight.newInsight('LIFECYCLE') + cy.get('.trends-insights-container canvas').should('exist') + }) + + it('can open a new SQL insight', () => { + insight.newInsight('SQL') + insight.updateQueryEditorText(hogQLQuery, 'hogql-query-editor') + cy.get('[data-attr="hogql-query-editor"]').should('exist') + cy.get('tr.DataTable__row').should('have.length.gte', 2) + }) + }) + }) +}) diff --git a/cypress/e2e/insights-navigation-open-sql-insight-first.cy.ts b/cypress/e2e/insights-navigation-open-sql-insight-first.cy.ts new file mode 100644 index 0000000000000..ca286d2e7c765 --- /dev/null +++ b/cypress/e2e/insights-navigation-open-sql-insight-first.cy.ts @@ -0,0 +1,84 @@ +import { urls } from 'scenes/urls' +import { decideResponse } from '../fixtures/api/decide' +import { insight } from '../productAnalytics' + +const hogQLQuery = `select event, + count() + from events + group by event, + properties.$browser, + person.properties.email + order by count() desc + limit 2` + +// For tests related to trends please check trendsElements.js +describe('Insights', () => { + beforeEach(() => { + cy.intercept('https://app.posthog.com/decide/*', (req) => + req.reply( + decideResponse({ + hogql: true, + 'data-exploration-insights': true, + }) + ) + ) + + cy.visit(urls.insightNew()) + }) + + describe('navigation', () => { + describe('opening a new insight after opening a new SQL insight', () => { + // TRICKY: these tests have identical assertions to the ones above, but we need to open a SQL insight first + // and then click a different tab to switch to that insight. + // this is because we had a bug where doing that would mean after starting to load the new insight, + // the SQL insight would be unexpectedly re-selected and the page would switch back to it + + beforeEach(() => { + insight.newInsight('SQL') + insight.updateQueryEditorText(hogQLQuery, 'hogql-query-editor') + cy.get('[data-attr="hogql-query-editor"]').should('exist') + cy.get('tr.DataTable__row').should('have.length.gte', 2) + }) + + it('can open a new trends insight', () => { + insight.clickTab('TRENDS') + cy.get('.trends-insights-container canvas').should('exist') + cy.get('tr').should('have.length.gte', 2) + cy.contains('tr', 'No insight results').should('not.exist') + }) + + it('can open a new funnels insight', () => { + insight.clickTab('FUNNELS') + cy.get('.funnels-empty-state__title').should('exist') + }) + + it('can open a new retention insight', () => { + insight.clickTab('RETENTION') + cy.get('.RetentionContainer canvas').should('exist') + cy.get('.RetentionTable__Tab').should('have.length', 66) + }) + + it('can open a new paths insight', () => { + insight.clickTab('PATH') + cy.get('.Paths g').should('have.length.gte', 5) // not a fixed value unfortunately + }) + + it('can open a new stickiness insight', () => { + insight.clickTab('STICKINESS') + cy.get('.trends-insights-container canvas').should('exist') + }) + + it('can open a new lifecycle insight', () => { + insight.clickTab('LIFECYCLE') + cy.get('.trends-insights-container canvas').should('exist') + }) + + it('can open a new SQL insight', () => { + insight.clickTab('SQL') + insight.updateQueryEditorText(hogQLQuery, 'hogql-query-editor') + cy.get('[data-attr="hogql-query-editor"]').should('exist') + cy.get('tr.DataTable__row').should('have.length.gte', 2) + }) + }) + }) +}) diff --git a/cypress/e2e/insights-navigation.cy.ts b/cypress/e2e/insights-navigation.cy.ts index 00ec829321bed..57a1853841533 100644 --- a/cypress/e2e/insights-navigation.cy.ts +++ b/cypress/e2e/insights-navigation.cy.ts @@ -48,101 +48,6 @@ describe('Insights', () => { cy.get('.RetentionTable__Tab').should('have.length', 66) }) - describe('opening a new insight directly', () => { - it('can open a new trends insight', () => { - insight.newInsight('TRENDS') - cy.get('.trends-insights-container canvas').should('exist') - cy.get('tr').should('have.length.gte', 2) - }) - - it('can open a new funnels insight', () => { - insight.newInsight('FUNNELS') - cy.get('.funnels-empty-state__title').should('exist') - }) - - it.skip('can open a new retention insight', () => { - insight.newInsight('RETENTION') - cy.get('.RetentionContainer canvas').should('exist') - cy.get('.RetentionTable__Tab').should('have.length', 66) - }) - - it('can open a new paths insight', () => { - insight.newInsight('PATHS') - cy.get('.Paths g').should('have.length.gte', 5) // not a fixed value unfortunately - }) - - it('can open a new stickiness insight', () => { - insight.newInsight('STICKINESS') - cy.get('.trends-insights-container canvas').should('exist') - }) - - it('can open a new lifecycle insight', () => { - insight.newInsight('LIFECYCLE') - cy.get('.trends-insights-container canvas').should('exist') - }) - - it('can open a new SQL insight', () => { - insight.newInsight('SQL') - insight.updateQueryEditorText(hogQLQuery, 'hogql-query-editor') - cy.get('[data-attr="hogql-query-editor"]').should('exist') - cy.get('tr.DataTable__row').should('have.length.gte', 2) - }) - }) - - describe('opening a new insight after opening a new SQL insight', () => { - // TRICKY: these tests have identical assertions to the ones above, but we need to open a SQL insight first - // and then click a different tab to switch to that insight. - // this is because we had a bug where doing that would mean after starting to load the new insight, - // the SQL insight would be unexpectedly re-selected and the page would switch back to it - - beforeEach(() => { - insight.newInsight('SQL') - insight.updateQueryEditorText(hogQLQuery, 'hogql-query-editor') - cy.get('[data-attr="hogql-query-editor"]').should('exist') - cy.get('tr.DataTable__row').should('have.length.gte', 2) - }) - - it('can open a new trends insight', () => { - insight.clickTab('TRENDS') - cy.get('.trends-insights-container canvas').should('exist') - cy.get('tr').should('have.length.gte', 2) - cy.contains('tr', 'No insight results').should('not.exist') - }) - - it('can open a new funnels insight', () => { - insight.clickTab('FUNNELS') - cy.get('.funnels-empty-state__title').should('exist') - }) - - it('can open a new retention insight', () => { - insight.clickTab('RETENTION') - cy.get('.RetentionContainer canvas').should('exist') - cy.get('.RetentionTable__Tab').should('have.length', 66) - }) - - it('can open a new paths insight', () => { - insight.clickTab('PATH') - cy.get('.Paths g').should('have.length.gte', 5) // not a fixed value unfortunately - }) - - it('can open a new stickiness insight', () => { - insight.clickTab('STICKINESS') - cy.get('.trends-insights-container canvas').should('exist') - }) - - it('can open a new lifecycle insight', () => { - insight.clickTab('LIFECYCLE') - cy.get('.trends-insights-container canvas').should('exist') - }) - - it('can open a new SQL insight', () => { - insight.clickTab('SQL') - insight.updateQueryEditorText(hogQLQuery, 'hogql-query-editor') - cy.get('[data-attr="hogql-query-editor"]').should('exist') - cy.get('tr.DataTable__row').should('have.length.gte', 2) - }) - }) - it('can open a new SQL insight and navigate to a different one, then back to SQL, and back again', () => { /** * This is here as a regression test. We had a bug where navigating to a new query based insight, diff --git a/cypress/e2e/insights.cy.ts b/cypress/e2e/insights.cy.ts index 5e4fc0063855b..bbffc9558e1e9 100644 --- a/cypress/e2e/insights.cy.ts +++ b/cypress/e2e/insights.cy.ts @@ -131,25 +131,6 @@ describe('Insights', () => { cy.get('[data-attr=insight-tags]').should('not.exist') }) - describe('insights date picker', () => { - it('Can set the date filter and show the right grouping interval', () => { - cy.get('[data-attr=date-filter]').click() - cy.get('div').contains('Yesterday').should('exist').click() - cy.get('[data-attr=interval-filter]').should('contain', 'Hour') - }) - - it('Can set a custom rolling date range', () => { - cy.get('[data-attr=date-filter]').click() - cy.get('[data-attr=rolling-date-range-input]').type('{selectall}5{enter}') - cy.get('[data-attr=rolling-date-range-date-options-selector]').click() - cy.get('.RollingDateRangeFilter__popover > div').contains('days').should('exist').click() - cy.get('.RollingDateRangeFilter__label').should('contain', 'In the last').click() - - // Test that the button shows the correct formatted range - cy.get('[data-attr=date-filter]').get('span').contains('Last 5 days').should('exist') - }) - }) - describe('view source', () => { it('can open the query editor', () => { insight.newInsight('TRENDS')