diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml new file mode 100644 index 00000000..dacb1822 --- /dev/null +++ b/.github/workflows/smoke.yml @@ -0,0 +1,56 @@ +name: smoke + +on: + push: + schedule: + # every 6 hours + - cron: 0 */6 * * * + +jobs: + smoke: + name: smoke + runs-on: ubuntu-latest + steps: + - name: test-slack + - name: checkout + uses: actions/checkout@v3 + - name: install pnpm + uses: pnpm/action-setup@v2 + - name: get pnpm store path + id: pnpm-cache + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + - uses: actions/cache@v3 + with: + path: | + ${{ steps.pnpm-cache.outputs.STORE_PATH }} + ~/.cache/Cypress + key: pnpm-cache-v1-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: pnpm-cache-v1- + - name: install deps + run: pnpm --filter @gertie/smoke-tests install + - name: setup just + uses: extractions/setup-just@v1 + - name: run smoke tests + run: just smoke-run + - name: upload-videos + uses: actions/upload-artifact@v3 + if: failure() + with: + name: smoke-videos + path: smoke/cypress/videos + - name: slack notify failure + if: failure() + run: | + curl -X POST \ + -H "Content-type: application/json; charset=utf-8" \ + -H "Authorization: Bearer $SLACK_API_TOKEN" \ + -d "{\"channel\":\"G6007H2NM\",\"text\":\"Gertrude smoke test *FAILED*\", \ + \"username\":\"Gertrude Bot\",\"icon_emoji\":\":fire_engine:\"}" \ + https://slack.com/api/chat.postMessage + +env: + CYPRESS_BASE_URL: https://parents.gertrude.app + CYPRESS_SMOKE_TEST_API_URL: https://api.gertrude.app + CYPRESS_SMOKE_TEST_EMAIL_INBOX_URL: ${{ secrets.SMOKE_TEST_EMAIL_INBOX_URL }} + SLACK_API_TOKEN: ${{ secrets.SLACK_API_TOKEN }} diff --git a/.gitignore b/.gitignore index 21cefffc..7fa14ecf 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ appviews/src/main.tsx storybook/storybook-static/ dash/app/build dash/app/cypress/screenshots +smoke/cypress.env.json **/cypress/downloads **/cypress/videos **/.next diff --git a/dash/components/src/Users/ConnectDeviceModal.tsx b/dash/components/src/Users/ConnectDeviceModal.tsx index 2256d3c0..0ccadd06 100644 --- a/dash/components/src/Users/ConnectDeviceModal.tsx +++ b/dash/components/src/Users/ConnectDeviceModal.tsx @@ -19,7 +19,10 @@ const ConnectModal: React.FC = ({ dismissAddDevice, request }) => (
Enter the code below into the Gertrude Mac App:
- + {payload.code}
diff --git a/justfile b/justfile index d9da1d99..6770f296 100644 --- a/justfile +++ b/justfile @@ -61,6 +61,12 @@ cy-open: cy-run: @pnpm cypress run --project dash/app +smoke-open: + @pnpm cypress open --project smoke + +smoke-run: + @pnpm cypress run --project smoke + test: @pnpm vitest run diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e03cd52..be348c5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -470,6 +470,12 @@ importers: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + smoke: + devDependencies: + cypress: + specifier: 13.6.1 + version: 13.6.1 + storybook: dependencies: '@argos-ci/puppeteer': @@ -10014,7 +10020,7 @@ packages: log-update: 4.0.0 p-map: 4.0.0 rfdc: 1.3.0 - rxjs: 7.5.7 + rxjs: 7.8.1 through: 2.3.8 wrap-ansi: 7.0.0 dev: true @@ -11838,12 +11844,6 @@ packages: dependencies: queue-microtask: 1.2.3 - /rxjs@7.5.7: - resolution: {integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==} - dependencies: - tslib: 2.4.1 - dev: true - /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4280fafe..fb0d0af1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,3 +5,4 @@ packages: - 'site/*' - 'shared/*' - 'storybook' + - 'smoke' diff --git a/smoke/cypress.config.ts b/smoke/cypress.config.ts new file mode 100644 index 00000000..128a8be3 --- /dev/null +++ b/smoke/cypress.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'cypress'; + +export default defineConfig({ + e2e: { baseUrl: `http://localhost:8081` }, + video: true, + viewportHeight: 800, +}); diff --git a/smoke/cypress/e2e/smoke.cy.ts b/smoke/cypress/e2e/smoke.cy.ts new file mode 100644 index 00000000..b9506edf --- /dev/null +++ b/smoke/cypress/e2e/smoke.cy.ts @@ -0,0 +1,80 @@ +/// +describe(`Smoke test`, () => { + const email = `82uii.smoke-test-${Date.now()}@inbox.testmail.app`; + const password = `_pw_${Date.now()}`; + + it(`critical flows`, () => { + // signup + cy.visit(`/signup`); + cy.get(`input[name=email]`).type(email); + cy.get(`input[name=password]`).type(`${password}{enter}`); + cy.contains(`Verification email sent`); + cy.wait(Cypress.env(`CI`) ? 10000 : 2500); + + // verify email + cy.request({ url: Cypress.env(`SMOKE_TEST_EMAIL_INBOX_URL`) }).then((response) => { + const body: string = response.body; + if (!body.includes(email)) { + throw new Error(`Expected to find email ${email} in ${body}`); + } + const match = body.match(/"([^" ]+verify-signup-email[^" ]+)"/); + if (!match) { + throw new Error(`Expected to find verify link in ${body}`); + } + cy.wrap(match[1]).as(`verifyLink`); + }); + cy.get(`@verifyLink`).then((verifyLink) => { + cy.visit({ url: verifyLink as any }); + }); + cy.contains(`Welcome to the parent website!`); + + // log out, then back in w/ email/pass + cy.contains(`Log out`).click(); + cy.visit(`/`); + cy.location(`pathname`).should(`eq`, `/login`); + cy.get(`input[name=email]`).type(email); + cy.get(`input[name=password]`).type(`${password}{enter}`); + cy.location(`pathname`).should(`eq`, `/`); + + // create a child + cy.contains(`Add a child`).click(); + cy.get(`[data-test=user-name]`).type(`Franny`); + cy.contains(`Save child`).click(); + + // get the connection code + cy.contains(`Get connection code`).click(); + cy.get(`[data-test=connection-code]`).invoke(`text`).as(`connectionCode`); + + // simulate that they installed the app and connected successfully + cy.get(`@connectionCode`).then((connectionCode) => { + cy.log(`connectionCode`, connectionCode); + cy.request({ + method: `POST`, + url: `${Cypress.env(`SMOKE_TEST_API_URL`)}/pairql/macos-app/ConnectUser`, + headers: { 'Content-Type': `application/json` }, + body: { + verificationCode: Number(connectionCode), + appVersion: `2.1.2`, + modelIdentifier: `MacBookPro16,1`, + username: `franny`, + fullUsername: `Franny`, + numericId: 502, + serialNumber: `C02ZL0J${Math.random()}`, + }, + }) + .its(`status`) + .should(`eq`, 200); + }); + + // dismiss connection modal + cy.get(`[data-test="modal-primary-btn"]`).click(); + cy.visit(`/children`); + cy.contains(`MacBook Pro`); + + // edit the user + cy.contains(`Edit`).click(); + cy.get(`[data-test=user-name]`).clear().type(`Franny (edited)`); + cy.contains(`Save child`).click(); + cy.contains(`Child saved`); + }); +}); diff --git a/smoke/cypress/support/commands.ts b/smoke/cypress/support/commands.ts new file mode 100644 index 00000000..6d4cbd5a --- /dev/null +++ b/smoke/cypress/support/commands.ts @@ -0,0 +1 @@ +/// diff --git a/smoke/cypress/support/e2e.ts b/smoke/cypress/support/e2e.ts new file mode 100644 index 00000000..1221b17e --- /dev/null +++ b/smoke/cypress/support/e2e.ts @@ -0,0 +1 @@ +import './commands'; diff --git a/smoke/package.json b/smoke/package.json new file mode 100644 index 00000000..29d8584d --- /dev/null +++ b/smoke/package.json @@ -0,0 +1,9 @@ +{ + "name": "@gertie/smoke-tests", + "version": "1.0.0", + "main": "src/index.ts", + "license": "ISC", + "devDependencies": { + "cypress": "13.6.1" + } +} diff --git a/smoke/readme.md b/smoke/readme.md new file mode 100644 index 00000000..936824d2 --- /dev/null +++ b/smoke/readme.md @@ -0,0 +1,10 @@ +# Gertie Smoke Tests + +For local testing, add a `./smoke/cypress.env.json` with values like: + +```json +{ + "SMOKE_TEST_EMAIL_INBOX_URL": "http://localhost:1111/test-email-inbox", + "SMOKE_TEST_API_URL": "http://localhost:2222" +} +``` diff --git a/smoke/tsconfig.json b/smoke/tsconfig.json new file mode 100644 index 00000000..3c43903c --- /dev/null +++ b/smoke/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.json" +}