Skip to content

Commit

Permalink
chore(e2e): create a test studio for e2e tests (#5044)
Browse files Browse the repository at this point in the history
* chore(e2e): create a test studio for e2e tests

* chore(e2e): use correct token

* chore(e2e): add basePath to testing studio

* chore(e2e): use test-studio as a dependency cleanup packages

* chore(e2e): remove unused dependency

* chore(e2e): run e2e tests in production build
  • Loading branch information
binoy14 authored and skogsmaskin committed Oct 31, 2023
1 parent 78a83eb commit bf64d10
Show file tree
Hide file tree
Showing 20 changed files with 283 additions and 19 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# You can generate your own token by heading over to the tokens-section of
# https://www.sanity.io/manage/, or by using your CLI user token (`sanity debug --secrets`)
SANITY_E2E_SESSION_TOKEN=
SANITY_E2E_PROJECT_ID=
SANITY_E2E_DATASET=

# Whether or not to run the end to end tests in headless mode. Defaults to true, but sometimes
# you might want to see the browser in action, in which case you can set this to `false`.
Expand Down
17 changes: 16 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,27 @@ jobs:
- name: Build CLI
run: yarn build:cli # Needed for CLI tests

- name: Build E2E test studio
env:
# Update the SANITY_E2E_SESSION_TOKEN on github to the new value once this is merged to next
# Change the below to `secrets.SANITY_E2E_SESSION_TOKEN`
# Delete `SANITY_E2E_SESSION_TOKEN_NEW` from github
SANITY_E2E_SESSION_TOKEN: ${{ secrets.SANITY_E2E_SESSION_TOKEN_NEW }}
SANITY_E2E_PROJECT_ID: ${{ secrets.SANITY_E2E_PROJECT_ID }}
SANITY_E2E_DATASET: ${{ secrets.SANITY_E2E_DATASET }}
run: yarn e2e:build

- name: Run end-to-end tests
env:
SANITY_E2E_SESSION_TOKEN: ${{ secrets.SANITY_E2E_SESSION_TOKEN }}
# Missing in docs but in use
# here https://github.com/microsoft/playwright/blob/main/packages/playwright/src/reporters/blob.ts#L108
PWTEST_BLOB_REPORT_NAME: ${{ matrix.project }}
# Update the SANITY_E2E_SESSION_TOKEN on github to the new value once this is merged to next
# Change the below to `secrets.SANITY_E2E_SESSION_TOKEN`
# Delete `SANITY_E2E_SESSION_TOKEN_NEW` from github
SANITY_E2E_SESSION_TOKEN: ${{ secrets.SANITY_E2E_SESSION_TOKEN_NEW }}
SANITY_E2E_PROJECT_ID: ${{ secrets.SANITY_E2E_PROJECT_ID }}
SANITY_E2E_DATASET: ${{ secrets.SANITY_E2E_DATASET }}
run: yarn test:e2e --project ${{ matrix.project }} --shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}

- uses: actions/upload-artifact@v3
Expand Down
3 changes: 3 additions & 0 deletions dev/studio-e2e-testing/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "@sanity/eslint-config-studio"
}
29 changes: 29 additions & 0 deletions dev/studio-e2e-testing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# Dependencies
/node_modules
/.pnp
.pnp.js

# Compiled Sanity Studio
/dist

# Temporary Sanity runtime, generated by the CLI on every dev server start
/.sanity

# Logs
/logs
*.log

# Coverage directory used by testing tools
/coverage

# Misc
.DS_Store
*.pem

# Typescript
*.tsbuildinfo

# Dotenv and similar local-only files
*.local
9 changes: 9 additions & 0 deletions dev/studio-e2e-testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Sanity Clean Content Studio

Congratulations, you have now installed the Sanity Content Studio, an open source real-time content editing environment connected to the Sanity backend.

Now you can do the following things:

- [Read “getting started” in the docs](https://www.sanity.io/docs/introduction/getting-started?utm_source=readme)
- [Join the community Slack](https://slack.sanity.io/?utm_source=readme)
- [Extend and build plugins](https://www.sanity.io/docs/content-studio/extending?utm_source=readme)
10 changes: 10 additions & 0 deletions dev/studio-e2e-testing/components/Branding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {Box, Text} from '@sanity/ui'
import React from 'react'

export function Branding() {
return (
<Box padding={3}>
<Text weight="bold">E2E Test Studio&trade;</Text>
</Box>
)
}
28 changes: 28 additions & 0 deletions dev/studio-e2e-testing/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "studio-e2e-testing",
"private": true,
"version": "3.18.1",
"license": "MIT",
"author": "Sanity.io <[email protected]>",
"scripts": {
"dev": "sanity dev --port 3339",
"start": "sanity start --port 3339",
"build": "sanity build"
},
"keywords": [
"sanity"
],
"dependencies": {
"@sanity/google-maps-input": "^3.0.1",
"@sanity/icons": "^2.4.0",
"@sanity/ui": "^1.7.2",
"@sanity/vision": "3.18.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-is": "^18.2.0",
"sanity": "3.18.1",
"sanity-plugin-mux-input": "^2.2.1",
"sanity-test-studio": "3.18.1",
"styled-components": "^6.1.0"
}
}
17 changes: 17 additions & 0 deletions dev/studio-e2e-testing/sanity.cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {defineCliConfig} from 'sanity/cli'
import {loadEnvFiles} from '../../scripts/utils/loadEnvFiles'

loadEnvFiles()

export default defineCliConfig({
api: {
projectId: process.env.SANITY_E2E_PROJECT_ID,
dataset: process.env.SANITY_E2E_DATASET,
},
vite: {
define: {
'process.env.SANITY_E2E_PROJECT_ID': JSON.stringify(process.env.SANITY_E2E_PROJECT_ID),
'process.env.SANITY_E2E_DATASET': JSON.stringify(process.env.SANITY_E2E_DATASET),
},
},
})
101 changes: 101 additions & 0 deletions dev/studio-e2e-testing/sanity.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {defineConfig, definePlugin} from 'sanity'
import {deskTool} from 'sanity/desk'
import {visionTool} from '@sanity/vision'
import {BookIcon} from '@sanity/icons'
import {muxInput} from 'sanity-plugin-mux-input'
import {googleMapsInput} from '@sanity/google-maps-input'
import {imageAssetSource} from 'sanity-test-studio/assetSources'
import {resolveDocumentActions as documentActions} from 'sanity-test-studio/documentActions'
import {resolveInitialValueTemplates} from 'sanity-test-studio/initialValueTemplates'
import {languageFilter} from 'sanity-test-studio/plugins/language-filter'
import {defaultDocumentNode, newDocumentOptions, structure} from 'sanity-test-studio/structure'
import {presenceTool} from 'sanity-test-studio/plugins/presence'
import {copyAction} from 'sanity-test-studio/fieldActions/copyAction'
import {assistFieldActionGroup} from 'sanity-test-studio/fieldActions/assistFieldActionGroup'
import {commentAction} from 'sanity-test-studio/fieldActions/commentFieldAction'
import {customInspector} from 'sanity-test-studio/inspectors/custom'
import {pasteAction} from 'sanity-test-studio/fieldActions/pasteAction'
import {Branding} from './components/Branding'
import {schemaTypes} from './schemas'

const sharedSettings = definePlugin({
name: 'sharedSettings',
schema: {
types: schemaTypes,
templates: resolveInitialValueTemplates,
},
form: {
image: {
assetSources: [imageAssetSource],
},
},
studio: {
components: {
logo: Branding,
},
},
document: {
actions: documentActions,
inspectors: (prev, ctx) => {
if (ctx.documentType === 'inspectorsTest') {
return [customInspector, ...prev]
}

return prev
},
unstable_fieldActions: (prev, ctx) => {
if (['fieldActionsTest', 'stringsTest'].includes(ctx.documentType)) {
return [...prev, commentAction, assistFieldActionGroup, copyAction, pasteAction]
}

return prev
},
newDocumentOptions,
},
plugins: [
deskTool({
icon: BookIcon,
name: 'content',
title: 'Content',
structure,
defaultDocumentNode,
}),
languageFilter({
defaultLanguages: ['nb'],
supportedLanguages: [
{id: 'ar', title: 'Arabic'},
{id: 'en', title: 'English'},
{id: 'nb', title: 'Norwegian (bokmål)'},
{id: 'nn', title: 'Norwegian (nynorsk)'},
{id: 'pt', title: 'Portuguese'},
{id: 'es', title: 'Spanish'},
],
types: ['languageFilterDebug'],
}),
googleMapsInput({
apiKey: 'AIzaSyDDO2FFi5wXaQdk88S1pQUa70bRtWuMhkI',
defaultZoom: 11,
defaultLocation: {
lat: 40.7058254,
lng: -74.1180863,
},
}),
visionTool({
defaultApiVersion: '2022-08-08',
}),
// eslint-disable-next-line camelcase
muxInput({mp4_support: 'standard'}),
presenceTool(),
],
})

export default defineConfig({
name: 'default',
title: 'studio-e2e-testing',

projectId: process.env.SANITY_E2E_PROJECT_ID!,
dataset: process.env.SANITY_E2E_DATASET!,

plugins: [sharedSettings()],
basePath: '/test',
})
1 change: 1 addition & 0 deletions dev/studio-e2e-testing/schemas/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {schemaTypes} from 'sanity-test-studio/schema'
1 change: 1 addition & 0 deletions dev/studio-e2e-testing/static/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Files placed here will be served by the Sanity server under the `/static`-prefix
20 changes: 20 additions & 0 deletions dev/studio-e2e-testing/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
"example:ecommerce-studio": "yarn --cwd examples/ecommerce-studio start",
"example:example-studio": "yarn --cwd dev/example-studio start",
"example:movies-studio": "yarn --cwd examples/movies-studio start",
"e2e:dev": "yarn --cwd dev/studio-e2e-testing dev",
"e2e:build": "yarn --cwd dev/studio-e2e-testing build",
"e2e:preview": "yarn e2e:build && yarn --cwd dev/studio-e2e-testing start",
"e2e:start": "yarn --cwd dev/studio-e2e-testing start",
"etl": "node -r dotenv-flow/config -r esbuild-register scripts/etl",
"init": "lerna clean --yes && run-s bootstrap build",
"lerna:clean": "lerna clean",
Expand Down
17 changes: 12 additions & 5 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ const OS_BROWSERS =
// Read environment variables
const CI = readBoolEnv('CI', false)
const E2E_DEBUG = readBoolEnv('SANITY_E2E_DEBUG', false)
const PROJECT_ID = 'ppsg7ml5'
const PROJECT_ID = process.env.SANITY_E2E_PROJECT_ID!

const BASE_URL = 'http://localhost:3339/'

/**
* See https://playwright.dev/docs/test-configuration.
Expand Down Expand Up @@ -52,7 +54,7 @@ export default defineConfig({
actionTimeout: 10000,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
baseURL: 'http://localhost:3333/',
baseURL: BASE_URL,
headless: readBoolEnv('SANITY_E2E_HEADLESS', !E2E_DEBUG),
storageState: getStorageStateForProjectId(PROJECT_ID),
viewport: {width: 1728, height: 1000},
Expand Down Expand Up @@ -87,9 +89,14 @@ export default defineConfig({

/* Run your local dev server before starting the tests */
webServer: {
command: 'yarn dev',
port: 3333,
/**
* If it is running in CI just start the production build assuming that studio is already build
* Locally run the dev server
*/
command: CI ? 'yarn e2e:start' : 'yarn e2e:dev',
port: 3339,
reuseExistingServer: !CI,
stdout: 'pipe',
},
})

Expand Down Expand Up @@ -123,7 +130,7 @@ function getStorageStateForProjectId(projectId: string) {
cookies: [],
origins: [
{
origin: 'http://localhost:3333',
origin: BASE_URL,
localStorage: [
{
name: `__studio_auth_token_${projectId}`,
Expand Down
8 changes: 6 additions & 2 deletions test/e2e/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# E2E Testing in the Studio

Before you get started with writing and running tests, you need to get hold of a token - either using your own Sanity user token (`sanity debug --secrets` will give you the CLI token provided you are logged in `sanity login`), or by creating a project API token using https://sanity.io/manage.
## Required Env Variables

The tests expects to find the token in an environment variable named `SANITY_E2E_SESSION_TOKEN`. Either define it in your shell, or add it to the `.env.local` file in the repository root.
The tests expects to find the below env variables. Either define it in your shell, or add it to the `.env.local` file in the repository root.

- `SANITY_E2E_SESSION_TOKEN`: Before you get started with writing and running tests, you need to get hold of a token - either using your own Sanity user token (`sanity debug --secrets` will give you the CLI token provided you are logged in `sanity login`), or by creating a project API token using https://sanity.io/manage.
- `SANITY_E2E_PROJECT_ID`: Project ID of the studio
- `SANITY_E2E_DATASET`: Dataset name of the studio

## Running tests

Expand Down
16 changes: 15 additions & 1 deletion test/e2e/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,25 @@ import {loadEnvFiles} from '../../scripts/utils/loadEnvFiles'
loadEnvFiles()

const SANITY_E2E_SESSION_TOKEN = process.env.SANITY_E2E_SESSION_TOKEN!
const SANITY_E2E_PROJECT_ID = process.env.SANITY_E2E_PROJECT_ID!
const SANITY_E2E_DATASET = process.env.SANITY_E2E_DATASET!

if (!SANITY_E2E_SESSION_TOKEN) {
console.error('Missing `SANITY_E2E_SESSION_TOKEN` environment variable.')
console.error('See `test/e2e/README.md` for details.')
process.exit(1)
}

export {SANITY_E2E_SESSION_TOKEN}
if (!SANITY_E2E_PROJECT_ID) {
console.error('Missing `SANITY_E2E_PROJECT_ID` environment variable.')
console.error('See `test/e2e/README.md` for details.')
process.exit(1)
}

if (!SANITY_E2E_DATASET) {
console.error('Missing `SANITY_E2E_DATASET` environment variable.')
console.error('See `test/e2e/README.md` for details.')
process.exit(1)
}

export {SANITY_E2E_SESSION_TOKEN, SANITY_E2E_PROJECT_ID, SANITY_E2E_DATASET}
2 changes: 1 addition & 1 deletion test/e2e/globalSetup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {chromium, type FullConfig} from '@playwright/test'

const INIT_TIMEOUT_MS = 120000
const FALLBACK_URL = 'http://localhost:3333/'
const FALLBACK_URL = 'http://localhost:3339/'

/**
* Global setup for all end-to-end tests.
Expand Down
2 changes: 0 additions & 2 deletions test/e2e/helpers/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
export const STUDIO_DATASET_NAME = 'test'
export const STUDIO_PROJECT_ID = 'ppsg7ml5'
export const STALE_TEST_THRESHOLD_MS = 10000
Loading

0 comments on commit bf64d10

Please sign in to comment.