Skip to content

Commit

Permalink
ci: parallel vue 3 tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum committed Aug 13, 2023
1 parent 34581fd commit ab7de69
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 12 deletions.
68 changes: 63 additions & 5 deletions .github/workflows/test-vue3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
dir: ./examples/vue3

jobs:
build-and-test:
build:
runs-on: ubuntu-latest
name: Build and test

env:
dir: ./examples/vue3
name: Build

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -57,9 +57,67 @@ jobs:
working-directory: ${{env.dir}}
run: pnpm run story:build

- name: Save build
uses: actions/upload-artifact@v3
with:
name: histoire-build
if-no-files-found: error
path: ${{env.dir}}/.histoire/dist
retention-days: 1


test:
runs-on: ubuntu-latest
name: Test
needs: build

strategy:
fail-fast: false
matrix:
containers: [0, 1, 2, 3, 4]

steps:
- uses: actions/checkout@v2

- name: Install node
uses: actions/setup-node@v2
with:
node-version: 16

- name: Install pnpm
uses: pnpm/action-setup@v2.2.4

- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Cache pnpm modules
uses: actions/cache@v2
with:
path: |
${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
~/.cache/Cypress
key: pnpm-v1-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
pnpm-v1-${{ runner.os }}-
- name: Install dependencies
run: pnpm install

- name: Download the build
uses: actions/download-artifact@v3
with:
name: histoire-build
path: ${{env.dir}}/.histoire/dist

- name: Run tests
working-directory: ${{env.dir}}
run: pnpm run ci
env:
# the number of containers in the job matrix
TOTAL_RUNNERS: 5
THIS_RUNNER: ${{ matrix.containers }}

- uses: actions/upload-artifact@v2
if: failure()
Expand Down
116 changes: 116 additions & 0 deletions examples/vue3/cypress-parallel.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// @TODO move to playwright

import fs from 'fs/promises'
import { globby } from 'globby'
import { minimatch } from 'minimatch'
import { exec } from 'child_process'

const totalRunners = parseInt(process.env.TOTAL_RUNNERS)
const thisRunner = parseInt(process.env.THIS_RUNNER)

// These are the same properties that are set in cypress.config.
// In practice, it's better to export these from another file, and
// import them here and in cypress.config, so that both files use
// the same values.
const specPatterns = {
specPattern: './cypress/integration/*.{ts,tsx,js,jsx}',
excludeSpecPattern: ['tsconfig.json'],
}

// used to roughly determine how many tests are in a file
const testPattern = /(^|\s)(it|test)\(/g

async function getTestCount (filePath) {
const content = await fs.readFile(filePath, 'utf8')
return content.match(testPattern)?.length || 0
}

// adapated from:
// https://github.com/bahmutov/find-cypress-specs/blob/main/src/index.js
async function getSpecFilePaths () {
const options = specPatterns

const files = await globby(options.specPattern, {
ignore: options.excludeSpecPattern,
})

// go through the files again and eliminate files that match
// the ignore patterns
const ignorePatterns = [...(options.excludeSpecPattern || [])]

// a function which returns true if the file does NOT match
// all of our ignored patterns
const doesNotMatchAllIgnoredPatterns = (file) => {
// using {dot: true} here so that folders with a '.' in them are matched
// as regular characters without needing an '.' in the
// using {matchBase: true} here so that patterns without a globstar **
// match against the basename of the file
const MINIMATCH_OPTIONS = { dot: true, matchBase: true }
return ignorePatterns.every((pattern) => {
return !minimatch(file, pattern, MINIMATCH_OPTIONS)
})
}

const filtered = files.filter(doesNotMatchAllIgnoredPatterns)

return filtered
}

async function sortSpecFilesByTestCount (specPathsOriginal) {
const specPaths = [...specPathsOriginal]

const testPerSpec = {}

for (const specPath of specPaths) {
testPerSpec[specPath] = await getTestCount(specPath)
}

return (
Object.entries(testPerSpec)
// Sort by the number of tests per spec file, so that we get a bit closer to
// splitting up the files evenly between the runners. It won't be perfect,
// but better than just splitting them randomly. And this will create a
// consistent file list/ordering so that file division is deterministic.
.sort((a, b) => b[1] - a[1])
.map((x) => x[0])
)
}

export function splitSpecs (specs, totalRunners, thisRunner) {
return specs.filter((_, index) => index % totalRunners === thisRunner)
}

(async () => {
try {
const specFilePaths = await sortSpecFilesByTestCount(await getSpecFilePaths())

if (!specFilePaths.length) {
throw Error('No spec files found.')
}

const specsToRun = splitSpecs(specFilePaths, totalRunners, thisRunner)

const command = `yarn cypress run --spec "${specsToRun.join(',')}"`

console.log(`Running: ${command}`)

const commandProcess = exec(command)

// pipe output because we want to see the results of the run

if (commandProcess.stdout) {
commandProcess.stdout.pipe(process.stdout)
}

if (commandProcess.stderr) {
commandProcess.stderr.pipe(process.stderr)
}

commandProcess.on('exit', (code) => {
process.exit(code || 0)
})
} catch (err) {
console.error(err)
process.exit(1)
}
})()
4 changes: 3 additions & 1 deletion examples/vue3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"story:build": "histoire build",
"story:preview": "histoire preview --port 4567",
"ci": "start-server-and-test story:preview http://localhost:4567/ test",
"test": "cypress run",
"test": "node ./cypress-parallel.mjs",
"test:dev": "cypress open --config baseUrl=http://localhost:6006",
"test:examples": "pnpm run story:build && pnpm run ci"
},
Expand All @@ -23,7 +23,9 @@
"@histoire/vendors": "workspace:*",
"@vitejs/plugin-vue": "^4.0.0",
"cypress": "^9.5.3",
"globby": "^13.2.2",
"histoire": "workspace:*",
"minimatch": "^9.0.3",
"nodemon": "^2.0.20",
"sass": "^1.50.0",
"start-server-and-test": "^1.14.0",
Expand Down
45 changes: 39 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ab7de69

Please sign in to comment.