Skip to content

Commit

Permalink
test: setup testing
Browse files Browse the repository at this point in the history
  • Loading branch information
maxnowack committed Feb 27, 2024
1 parent 68cbec9 commit f417716
Show file tree
Hide file tree
Showing 9 changed files with 1,852 additions and 77 deletions.
9 changes: 8 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,14 @@ module.exports = {
*/
// Overrides
'import/no-extraneous-dependencies': ['error', {
devDependencies: ['*.config.ts'],
devDependencies: [
'.eslintrc.js',
'**/*.test.ts',
'**/*.spec.ts',
'__tests__/**/*.ts',
'*.config.ts',
'**/vite.config.ts',
],
}],
'arrow-parens': ['error', 'as-needed', { requireForBlockBody: true }],
'func-names': 'error', // Changed from 'warn' to 'error'.
Expand Down
162 changes: 162 additions & 0 deletions .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
name: Checks

on:
push:
branches:
- main
- development
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
preflight:
if: ${{ github.event.pull_request.head.ref != 'main' && github.event.pull_request.head.ref != 'development' }}
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 21.x]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Load node modules cache
id: modules-cache
uses: actions/cache@v4
timeout-minutes: 5
continue-on-error: true
with:
path: |
**/node_modules
key: ${{ runner.OS }}-modules-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-modules-${{ matrix.node-version }}-
- name: install modules
run: |
npm install --no-audit --force --loglevel=error --no-update-notifier
linting:
needs: preflight
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: latest
- name: Load node modules cache
id: modules-cache
uses: actions/cache/restore@v4
timeout-minutes: 5
continue-on-error: false
with:
fail-on-cache-miss: true
path: |
**/node_modules
key: ${{ runner.OS }}-modules-20.x-${{ hashFiles('**/package-lock.json') }}
- name: linting
run: |
npm run build
npm run lint
typecheck:
needs: preflight
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: latest
- name: Load node modules cache
id: modules-cache
uses: actions/cache/restore@v4
timeout-minutes: 5
continue-on-error: false
with:
fail-on-cache-miss: true
path: |
**/node_modules
key: ${{ runner.OS }}-modules-20.x-${{ hashFiles('**/package-lock.json') }}
- name: typecheck
run: |
npm run build
npm run type-check
unit-tests:
needs: preflight
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 21.x]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Load node modules cache
id: modules-cache
uses: actions/cache/restore@v4
timeout-minutes: 5
continue-on-error: false
with:
fail-on-cache-miss: true
path: |
**/node_modules
key: ${{ runner.OS }}-modules-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
- name: unit testing
run: |
npm run build
npm test -- --coverage run
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

build:
needs: preflight
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 21.x]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Load node modules cache
id: modules-cache
uses: actions/cache/restore@v4
timeout-minutes: 5
continue-on-error: false
with:
fail-on-cache-miss: true
path: |
**/node_modules
key: ${{ runner.OS }}-modules-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
- name: build package
run: |
npm run build
build-docs:
needs: preflight
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.5.1'
- name: Load node modules cache
id: modules-cache
uses: actions/cache/restore@v4
timeout-minutes: 5
continue-on-error: false
with:
fail-on-cache-miss: true
path: |
**/node_modules
key: ${{ runner.OS }}-modules-20.x-${{ hashFiles('**/package-lock.json') }}
- name: build docs
run: |
npm run docs:build
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/tmp
dist
node_modules
coverage
215 changes: 215 additions & 0 deletions __tests__/cli.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-await-in-loop */
import fs from 'fs'
import path from 'path'
import { describe, it, expect, afterAll, vi } from 'vitest'
import temp from 'temp'
import executeCommand from '../src/utils/executeCommand.js'
import { add, update, commit, remove } from '../src/commands.js'
import { createConfig, parseConfig } from '../src/config'
import latestHash from '../src/utils/git/latestHash.js'

const exists = async (file: string) => fs.promises.access(file)
.then(() => true)
.catch(() => false)

async function setupGit(
gitFiles: Record<string, string>,
dependencies: {
repo?: string,
hash?: string,
remotePath: string,
localPath: string,
}[] = [],
) {
const testFolder = temp.mkdirSync()
const gitFolder = temp.mkdirSync()

await executeCommand('git init', { cwd: gitFolder })
await executeCommand('git config user.email "[email protected]"', { cwd: gitFolder })
await executeCommand('git config user.name "Test User"', { cwd: gitFolder })
await executeCommand('git config receive.denyCurrentBranch ignore', { cwd: gitFolder })
for (const [file, content] of Object.entries(gitFiles)) {
await fs.promises.writeFile(path.join(gitFolder, file), content)
}

await executeCommand('git add .', { cwd: gitFolder })
await executeCommand('git commit -m "initial commit"', { cwd: gitFolder })
const hash = await latestHash(gitFolder)

await fs.promises.writeFile(
path.join(testFolder, 'blend.yml'),
createConfig({
dependencies: dependencies.map(dep => ({
repo: dep.repo ?? gitFolder,
hash: dep.hash ?? hash,
remotePath: dep.remotePath,
localPath: dep.localPath,
})),
}),
)
await expect(exists(path.join(testFolder, 'blend.yml'))).resolves.toBe(true)

vi.spyOn(process, 'cwd').mockReturnValue(testFolder)
if (dependencies.length > 0) await update()
for (const dep of dependencies) {
const localPath = path.join(testFolder, dep.localPath)
await expect(exists(localPath)).resolves.toBe(true)
}

return { testFolder, gitFolder }
}

describe('blend cli', () => {
describe('add', () => {
it('should add a dependency', async () => {
const { testFolder, gitFolder } = await setupGit({ 'test.txt': 'test' })
await expect(add(gitFolder, 'test.txt')).resolves.not.toThrow()
await expect(exists(path.join(testFolder, 'test.txt'))).resolves.toBe(true)

await expect(exists(path.join(testFolder, 'blend.yml'))).resolves.toBe(true)
const config = parseConfig(await fs.promises.readFile(
path.join(testFolder, 'blend.yml'),
'utf-8',
))
expect(config).toMatchObject({
dependencies: [{
repo: gitFolder,
remotePath: 'test.txt',
localPath: 'test.txt',
}],
})
})

it('should add a dependency with a local path', async () => {
const { testFolder, gitFolder } = await setupGit({ 'test.txt': 'test' })

await expect(add(gitFolder, 'test.txt', 'test2.txt')).resolves.not.toThrow()
await expect(exists(path.join(testFolder, 'test2.txt'))).resolves.toBe(true)

await expect(exists(path.join(testFolder, 'blend.yml'))).resolves.toBe(true)
const config = parseConfig(await fs.promises.readFile(
path.join(testFolder, 'blend.yml'),
'utf-8',
))
expect(config).toMatchObject({
dependencies: [{
repo: gitFolder,
remotePath: 'test.txt',
localPath: 'test2.txt',
}],
})
})

it('should add a dependency with a branch', async () => {
const { testFolder, gitFolder } = await setupGit({ 'test.txt': 'test' })

await expect(add(`${gitFolder}#main`, 'test.txt', 'test3.txt')).resolves.not.toThrow()

Check failure on line 107 in __tests__/cli.spec.ts

View workflow job for this annotation

GitHub Actions / unit-tests (18.x)

AssertionError: promise rejected "Error: command 'git checkout main exited …" instead of resolving

at __tests__/cli.spec.ts:107:69

Check failure on line 107 in __tests__/cli.spec.ts

View workflow job for this annotation

GitHub Actions / unit-tests (20.x)

AssertionError: promise rejected "Error: command 'git checkout main exited …" instead of resolving

at __tests__/cli.spec.ts:107:69

Check failure on line 107 in __tests__/cli.spec.ts

View workflow job for this annotation

GitHub Actions / unit-tests (21.x)

AssertionError: promise rejected "Error: command 'git checkout main exited …" instead of resolving

at __tests__/cli.spec.ts:107:69
await expect(exists(path.join(testFolder, 'test3.txt'))).resolves.toBe(true)

await expect(exists(path.join(testFolder, 'blend.yml'))).resolves.toBe(true)
const config = parseConfig(await fs.promises.readFile(
path.join(testFolder, 'blend.yml'),
'utf-8',
))
expect(config).toMatchObject({
dependencies: [{
repo: `${gitFolder}#main`,
remotePath: 'test.txt',
localPath: 'test3.txt',
}],
})
})

it('should fail if the local path already exists', async () => {
const { gitFolder } = await setupGit({ 'test.txt': 'test' }, [{
remotePath: 'test.txt',
localPath: 'test.txt',
}])
await expect(add(gitFolder, 'test.txt')).rejects.toThrowError()
})

it('should fail if the repository does not exist', async () => {
await setupGit({ 'test.txt': 'test' })
await expect(add('/tmp/does-not-exist', 'test.txt')).rejects.toThrowError()
})

it('should fail if the remote path does not exist', async () => {
const { gitFolder } = await setupGit({ 'test.txt': 'test' })
await expect(add(gitFolder, 'does-not-exist')).rejects.toThrowError()
})
})

describe('update', () => {
it('should update all dependencies', async () => {
await setupGit({ 'test.txt': 'test' }, [{
remotePath: 'test.txt',
localPath: 'test.txt',
}])
await expect(update()).resolves.not.toThrow()
})

it('should fail if there are no dependencies', async () => {
await setupGit({ 'test.txt': 'test' })
await expect(update()).rejects.toThrowError()
})
})

describe('commit', () => {
it('should fail if the dependency has no changes', async () => {
await setupGit({ 'test.txt': 'test' }, [{
remotePath: 'test.txt',
localPath: 'test.txt',
}])
await expect(commit('test.txt', 'commit message')).rejects.toThrowError()
})

it('should fail if the commit message is empty', async () => {
await setupGit({ 'test.txt': 'test' }, [{
remotePath: 'test.txt',
localPath: 'test.txt',
}])
await expect(commit('test.txt', '')).rejects.toThrowError()
})

it('should commit a dependency', async () => {
const { testFolder } = await setupGit({ 'test.txt': 'test' }, [{
remotePath: 'test.txt',
localPath: 'test.txt',
}])
await fs.promises.writeFile(path.join(testFolder, 'test.txt'), 'test2')
console.log('TestFolder', testFolder)
await expect(commit('test.txt', 'commit message')).resolves.not.toThrow()

Check failure on line 182 in __tests__/cli.spec.ts

View workflow job for this annotation

GitHub Actions / unit-tests (18.x)

AssertionError: promise rejected "Error: command 'git commit -m "commit mes…" instead of resolving

at __tests__/cli.spec.ts:182:56

Check failure on line 182 in __tests__/cli.spec.ts

View workflow job for this annotation

GitHub Actions / unit-tests (20.x)

AssertionError: promise rejected "Error: command 'git commit -m "commit mes…" instead of resolving

at __tests__/cli.spec.ts:182:56

Check failure on line 182 in __tests__/cli.spec.ts

View workflow job for this annotation

GitHub Actions / unit-tests (21.x)

AssertionError: promise rejected "Error: command 'git commit -m "commit mes…" instead of resolving

at __tests__/cli.spec.ts:182:56
})

it('should fail if the dependency does not exist', async () => {
await setupGit({ 'test.txt': 'test' }, [{
remotePath: 'test.txt',
localPath: 'test.txt',
}])
await expect(commit('does-not-exist', 'commit message')).rejects.toThrowError()
})
})

describe('remove', () => {
it('should remove a dependency', async () => {
await setupGit({ 'test.txt': 'test' }, [{
remotePath: 'test.txt',
localPath: 'test.txt',
}])
await expect(remove('test.txt')).resolves.not.toThrow()
})

it('should fail if the dependency does not exist', async () => {
await setupGit({ 'test.txt': 'test' }, [{
remotePath: 'test.txt',
localPath: 'test.txt',
}])
await expect(remove('does-not-exist')).rejects.toThrowError()
})
})

afterAll(() => {
temp.cleanupSync()
})
})
Loading

0 comments on commit f417716

Please sign in to comment.