Skip to content

Commit

Permalink
Merge branch 'master' into feat/flush-on-revoke
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite committed Sep 19, 2023
2 parents a1ed4fd + e549fba commit eb3e39d
Show file tree
Hide file tree
Showing 447 changed files with 6,912 additions and 3,157 deletions.
8 changes: 6 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ module.exports = {
element: 'a',
message: 'use <Link> instead',
},
{
element: 'ReactMarkdown',
message: 'use <LemonMarkdown> instead',
},
],
},
],
Expand Down Expand Up @@ -175,9 +179,9 @@ module.exports = {
message: 'use <LemonCollapse> instead',
},
{
element:'MonacoEditor',
element: 'MonacoEditor',
message: 'use <CodeEditor> instead',
}
},
],
},
],
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 18 additions & 2 deletions .github/workflows/storybook-chromatic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,24 @@ jobs:
if [ $ADDED -gt 0 ] || [ $MODIFIED -gt 0 ]; then
echo "Snapshots updated ($ADDED new, $MODIFIED changed), running OptiPNG"
apt update && apt install -y optipng
git add frontend/__snapshots__/ playwright/
pnpm lint-staged
optipng -clobber -o4 -strip all
# we don't want to _always_ run OptiPNG
# so, we run it after checking for a diff
# but, the files we diffed might then be changed by OptiPNG
# and as a result they might no longer be different...
# we check again
git diff --name-status frontend/__snapshots__/ # For debugging
ADDED=$(git diff --name-status frontend/__snapshots__/ | grep '^A' | wc -l)
MODIFIED=$(git diff --name-status frontend/__snapshots__/ | grep '^M' | wc -l)
DELETED=$(git diff --name-status frontend/__snapshots__/ | grep '^D' | wc -l)
TOTAL=$(git diff --name-status frontend/__snapshots__/ | wc -l)
if [ $ADDED -gt 0 ] || [ $MODIFIED -gt 0 ]; then
echo "Snapshots updated ($ADDED new, $MODIFIED changed), _even after_ running OptiPNG"
git add frontend/__snapshots__/ playwright/
fi
fi
echo "${{ matrix.browser }}-${{ matrix.shard }}-added=$ADDED" >> $GITHUB_OUTPUT
Expand Down
12 changes: 0 additions & 12 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

# Check if staged files contain any added or modified PNGs - skip when merging
if \
git rev-parse -q --verify MERGE_HEAD \
&& git diff --cached --name-status | grep '^[AM]' | grep -q '.png$'
then
# Error if OptiPNG is not installed
if ! command -v optipng >/dev/null; then
echo "PNG files must be optimized before being committed, but OptiPNG is not installed! Fix this with \`brew/apt install optipng\`."
exit 1
fi
fi

pnpm lint-staged
11 changes: 11 additions & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ const setupMsw = () => {
// Make sure the msw worker is started
worker.start({
quiet: true,
onUnhandledRequest(request, print) {
// MSW warns on all unhandled requests, but we don't necessarily care
const pathAllowList = ['/images/']

if (pathAllowList.some((path) => request.url.pathname.startsWith(path))) {
return
}

// Otherwise, default MSW warning behavior
print.warning()
},
})
;(window as any).__mockServiceWorker = worker
;(window as any).POSTHOG_APP_CONTEXT = getStorybookAppContext()
Expand Down
5 changes: 3 additions & 2 deletions .storybook/test-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ module.exports = {
const storyContext = (await getStoryContext(page, context)) as StoryContext
const { skip = false, snapshotBrowsers = ['chromium'] } = storyContext.parameters?.testOptions ?? {}

browserContext.setDefaultTimeout(3000) // Reduce the default timeout from 30 s to 3 s to pre-empt Jest timeouts
browserContext.setDefaultTimeout(5000) // Reduce the default timeout from 30 s to 5 s to pre-empt Jest timeouts
if (!skip) {
const currentBrowser = browserContext.browser()!.browserType().name() as SupportedBrowserName
if (snapshotBrowsers.includes(currentBrowser)) {
Expand Down Expand Up @@ -208,7 +208,8 @@ async function expectLocatorToMatchStorySnapshot(
// Compare structural similarity instead of raw pixels - reducing false positives
// See https://github.com/americanexpress/jest-image-snapshot#recommendations-when-using-ssim-comparison
comparisonMethod: 'ssim',
failureThreshold: 0.0003,
// 0.01 would be a 1% difference
failureThreshold: 0.01,
failureThresholdType: 'percent',
})
}
10 changes: 9 additions & 1 deletion bin/plugin-server
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ if [ $? -ne 0 ]; then
exit 1
fi

[[ -n $DEBUG ]] && cmd="pnpm start:dev" || cmd="node dist/index.js"
if [[ -n $DEBUG ]]; then
if [[ -n $NO_WATCH ]]; then
cmd="pnpm start:devNoWatch"
else
cmd="pnpm start:dev"
fi
else
cmd="node dist/index.js"
fi

if [[ -n $NO_RESTART_LOOP ]]; then
echo "▶️ Starting plugin server..."
Expand Down
54 changes: 54 additions & 0 deletions cypress/e2e/auth-password-reset.cy.ts
Original file line number Diff line number Diff line change
@@ -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('[email protected]').should('have.value', '[email protected]').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('[email protected]')
cy.get('button[type=submit]').click()
cy.get('div').should('contain', 'Request received successfully!')
cy.get('b').should('contain', '[email protected]')
})

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')
})
})
55 changes: 0 additions & 55 deletions cypress/e2e/auth.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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('[email protected]').should('have.value', '[email protected]').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('[email protected]')
cy.get('button[type=submit]').click()
cy.get('div').should('contain', 'Request received successfully!')
cy.get('b').should('contain', '[email protected]')
})

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')
})
})
61 changes: 61 additions & 0 deletions cypress/e2e/dashboard-deletion.ts
Original file line number Diff line number Diff line change
@@ -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)
})
})
})
Loading

0 comments on commit eb3e39d

Please sign in to comment.