Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(e2e): create a test studio for e2e tests #5044

Merged
merged 6 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading