From 2990914cfd3e9305a92a4554cb691bad91485774 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:31:05 -0400 Subject: [PATCH 01/10] Add some fixture utils and assert that the .prettierrc.js is generated correctly for all tests, ensuring that the fixture utils work --- tests/.eslintignore | 1 + tests/.prettierignore | 1 + tests/assertions.ts | 44 ++++++++++++++++++++++++++- tests/fixtures/default/.prettierrc.js | 5 +++ tests/utils.ts | 20 ++++++++++++ 5 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/.eslintignore create mode 100644 tests/.prettierignore create mode 100644 tests/fixtures/default/.prettierrc.js diff --git a/tests/.eslintignore b/tests/.eslintignore new file mode 100644 index 00000000..67fc140b --- /dev/null +++ b/tests/.eslintignore @@ -0,0 +1 @@ +fixtures/ diff --git a/tests/.prettierignore b/tests/.prettierignore new file mode 100644 index 00000000..67fc140b --- /dev/null +++ b/tests/.prettierignore @@ -0,0 +1 @@ +fixtures/ diff --git a/tests/assertions.ts b/tests/assertions.ts index 1cdbee83..f953355d 100644 --- a/tests/assertions.ts +++ b/tests/assertions.ts @@ -1,8 +1,9 @@ import fse from 'fs-extra'; +import fs from 'node:fs/promises'; import path from 'node:path'; import { expect } from 'vitest'; -import { packageJsonAt } from './utils.js'; +import { fixture, packageJsonAt } from './utils.js'; interface AssertGeneratedOptions { projectRoot: string; @@ -50,4 +51,45 @@ export async function assertGeneratedCorrectly({ expect(await fse.pathExists(pathToFile), `${pathToFile} exists`).toBe(true); } + + await matchesFixture('.prettierrc.js', { cwd: projectRoot }); +} + +export async function matchesFixture( + /** + * Project-relative file to test against + */ + testFilePath: string, + options?: { + /** + * Which fixture set to use + */ + scenario?: string; + /** + * By default, the file used will be the same as the testFilePath, but + * in the fixtures directory under the (maybe) specified scenario. + * this can be overridden, if needed. + * (like if you're testFilePath is deep with in an existing monorepo, and wouldn't + * inherently match our default-project structure used in the fixtures) + */ + file?: string; + + /** + * The working directory to use for the relative paths. Defaults to process.cwd() (node default) + */ + cwd?: string; + } +) { + let scenario = options?.scenario ?? 'default'; + let fixtureFile = options?.file ?? testFilePath; + + if (options?.cwd) { + testFilePath = path.join(options.cwd, testFilePath); + fixtureFile = path.join(options.cwd, fixtureFile); + } + + let sourceContents = await fs.readFile(testFilePath); + let fixtureContents = await fixture(fixtureFile, { scenario }); + + expect(sourceContents).to.equal(fixtureContents, `${testFilePath} matches ${fixtureFile}`); } diff --git a/tests/fixtures/default/.prettierrc.js b/tests/fixtures/default/.prettierrc.js new file mode 100644 index 00000000..534e6d35 --- /dev/null +++ b/tests/fixtures/default/.prettierrc.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = { + singleQuote: true, +}; diff --git a/tests/utils.ts b/tests/utils.ts index 2067da71..c2f6d1a1 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -8,9 +8,29 @@ import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const blueprintPath = path.join(__dirname, '..'); +const fixturesPath = path.join(__dirname, 'fixtures'); export const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm'] as const; +export async function fixture( + /** + * Which file within in the fixture-set / scenario to read + */ + file: string, + options?: { + /** + * Which fixture set to use + */ + scenario?: string; + } +) { + let scenario = options?.scenario ?? 'default'; + let fixtureFilePath = path.join(fixturesPath, scenario, file); + let contents = await fs.readFile(fixtureFilePath); + + return contents.toString(); +} + export async function createTmp() { let prefix = 'v2-addon-blueprint--'; let prefixPath = path.join(os.tmpdir(), prefix); From 3787585bd3bd179734d7aa1fcb002aa046c95ce7 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 14 Jul 2023 14:41:03 -0400 Subject: [PATCH 02/10] toString the buffer --- tests/assertions.ts | 5 ++++- tests/smoke-tests/--typescript.test.ts | 3 ++- tests/utils.ts | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/assertions.ts b/tests/assertions.ts index f953355d..c204d68d 100644 --- a/tests/assertions.ts +++ b/tests/assertions.ts @@ -91,5 +91,8 @@ export async function matchesFixture( let sourceContents = await fs.readFile(testFilePath); let fixtureContents = await fixture(fixtureFile, { scenario }); - expect(sourceContents).to.equal(fixtureContents, `${testFilePath} matches ${fixtureFile}`); + expect(sourceContents.toString()).to.equal( + fixtureContents, + `${testFilePath} matches ${fixtureFile}` + ); } diff --git a/tests/smoke-tests/--typescript.test.ts b/tests/smoke-tests/--typescript.test.ts index b0c07c60..57328eab 100644 --- a/tests/smoke-tests/--typescript.test.ts +++ b/tests/smoke-tests/--typescript.test.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs/promises'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; @@ -34,7 +35,7 @@ for (let packageManager of SUPPORTED_PACKAGE_MANAGERS) { }); afterAll(async () => { - // await fs.rm(tmpDir, { recursive: true, force: true }); + await fs.rm(tmpDir, { recursive: true, force: true }); }); it('was generated correctly', async () => { diff --git a/tests/utils.ts b/tests/utils.ts index c2f6d1a1..50ec6093 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -25,7 +25,7 @@ export async function fixture( } ) { let scenario = options?.scenario ?? 'default'; - let fixtureFilePath = path.join(fixturesPath, scenario, file); + let fixtureFilePath = path.isAbsolute(file) ? file : path.join(fixturesPath, scenario, file); let contents = await fs.readFile(fixtureFilePath); return contents.toString(); From 72154cb03becd89c5310c26e6c850a889ec3dc83 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 15 Jul 2023 11:52:22 -0400 Subject: [PATCH 03/10] Move lint ignore entries to the directories where eslint and prettier are ran from. ignore files do not cascade --- .eslintignore | 1 + .prettierignore | 1 + tests/.eslintignore | 1 - tests/.prettierignore | 1 - 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 .prettierignore delete mode 100644 tests/.eslintignore delete mode 100644 tests/.prettierignore diff --git a/.eslintignore b/.eslintignore index a37273b3..3b6f3399 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ files/ +tests/fixtures/ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..55ccdbd9 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +tests/fixtures/ diff --git a/tests/.eslintignore b/tests/.eslintignore deleted file mode 100644 index 67fc140b..00000000 --- a/tests/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -fixtures/ diff --git a/tests/.prettierignore b/tests/.prettierignore deleted file mode 100644 index 67fc140b..00000000 --- a/tests/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -fixtures/ From 9f5046b39b06af99d47cec0342ea80be8c89ebf7 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 15 Jul 2023 12:16:08 -0400 Subject: [PATCH 04/10] Add assertion about missing fixture file --- tests/utils.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/utils.ts b/tests/utils.ts index 50ec6093..19c8ab8d 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -1,5 +1,6 @@ import { type Options, execa } from 'execa'; import fse from 'fs-extra'; +import assert from 'node:assert'; import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; @@ -26,6 +27,14 @@ export async function fixture( ) { let scenario = options?.scenario ?? 'default'; let fixtureFilePath = path.isAbsolute(file) ? file : path.join(fixturesPath, scenario, file); + + let exists = await fse.pathExists(fixtureFilePath); + + assert( + exists, + `Fixture file ${fixtureFilePath} does not exist. To make this work, place a new file ${fixtureFilePath} in the tests/fixtures/${scenario} directory` + ); + let contents = await fs.readFile(fixtureFilePath); return contents.toString(); From 8d73b488744f915ae7fc1889253bb814fda89902 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 15 Jul 2023 12:58:07 -0400 Subject: [PATCH 05/10] Improve error message --- tests/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils.ts b/tests/utils.ts index 19c8ab8d..27a4b8cd 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -32,7 +32,7 @@ export async function fixture( assert( exists, - `Fixture file ${fixtureFilePath} does not exist. To make this work, place a new file ${fixtureFilePath} in the tests/fixtures/${scenario} directory` + `Fixture file ${file} does not exist. To make this work, place a new file ${file} in the tests/fixtures/${scenario} directory` ); let contents = await fs.readFile(fixtureFilePath); From 4a82bb9096e3299f084375748540d465baae52f9 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 15 Jul 2023 13:00:15 -0400 Subject: [PATCH 06/10] Improve error message --- tests/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils.ts b/tests/utils.ts index 27a4b8cd..9c0341d3 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -32,7 +32,7 @@ export async function fixture( assert( exists, - `Fixture file ${file} does not exist. To make this work, place a new file ${file} in the tests/fixtures/${scenario} directory` + `Fixture file '${file}' does not exist. To make this work, place a new file '${file}' in the 'tests/fixtures/${scenario}' directory. Checked the absolute path: '${fixtureFilePath}'.` ); let contents = await fs.readFile(fixtureFilePath); From 3852609be6db1186c5ed3003d48f251c3e2298cd Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sat, 15 Jul 2023 16:11:29 -0400 Subject: [PATCH 07/10] Demonstrate usage with fixturify --- tests/assertions.ts | 12 ++++++++---- .../custom-locations.test.ts | 8 ++------ .../within-existing-monorepo/defaults.test.ts | 7 ++----- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/tests/assertions.ts b/tests/assertions.ts index c204d68d..f1461ed4 100644 --- a/tests/assertions.ts +++ b/tests/assertions.ts @@ -85,14 +85,18 @@ export async function matchesFixture( if (options?.cwd) { testFilePath = path.join(options.cwd, testFilePath); - fixtureFile = path.join(options.cwd, fixtureFile); } - let sourceContents = await fs.readFile(testFilePath); + let sourceContents = (await fs.readFile(testFilePath)).toString(); let fixtureContents = await fixture(fixtureFile, { scenario }); - expect(sourceContents.toString()).to.equal( - fixtureContents, + /** + * We trim because whether or not the source or fixture has + * leading / trailing invisible characters is of no significance + * and is mostly a bother to get correct in testing + */ + expect(sourceContents.trim()).to.equal( + fixtureContents.trim(), `${testFilePath} matches ${fixtureFile}` ); } diff --git a/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts b/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts index 786616c9..09a99435 100644 --- a/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts +++ b/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts @@ -4,14 +4,10 @@ import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { assertGeneratedCorrectly } from '../../assertions.js'; -import { createAddon, createTmp, install, runScript } from '../../utils.js'; +import { createAddon, createTmp, fixture, install, runScript } from '../../utils.js'; let commonFixtures = { - '.prettierrc.js': - // prettier-ignore - 'module.exports = {' + - ' singleQuote: true,' + - '};', + '.prettierrc.js': await fixture('.prettierrc.js'), }; describe('custom locations', () => { diff --git a/tests/smoke-tests/within-existing-monorepo/defaults.test.ts b/tests/smoke-tests/within-existing-monorepo/defaults.test.ts index ee894988..b543eef2 100644 --- a/tests/smoke-tests/within-existing-monorepo/defaults.test.ts +++ b/tests/smoke-tests/within-existing-monorepo/defaults.test.ts @@ -7,17 +7,14 @@ import { assertGeneratedCorrectly } from '../../assertions.js'; import { createAddon, createTmp, + fixture, install, runScript, SUPPORTED_PACKAGE_MANAGERS, } from '../../utils.js'; let commonFixtures = { - '.prettierrc.js': - // prettier-ignore - 'module.exports = {' + - ' singleQuote: true,' + - '};', + '.prettierrc.js': await fixture('.prettierrc.js'), }; for (let packageManager of SUPPORTED_PACKAGE_MANAGERS) { From 6ea52bcf3d7cc6e9f9175874858c7f53597ead28 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sun, 16 Jul 2023 10:23:39 -0400 Subject: [PATCH 08/10] Rename fixture to readFixture and add a comment block describe how the fixtures / scenarios work --- tests/assertions.ts | 4 ++-- tests/utils.ts | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/assertions.ts b/tests/assertions.ts index f1461ed4..94783f4b 100644 --- a/tests/assertions.ts +++ b/tests/assertions.ts @@ -3,7 +3,7 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { expect } from 'vitest'; -import { fixture, packageJsonAt } from './utils.js'; +import { packageJsonAt, readFixture } from './utils.js'; interface AssertGeneratedOptions { projectRoot: string; @@ -88,7 +88,7 @@ export async function matchesFixture( } let sourceContents = (await fs.readFile(testFilePath)).toString(); - let fixtureContents = await fixture(fixtureFile, { scenario }); + let fixtureContents = await readFixture(fixtureFile, { scenario }); /** * We trim because whether or not the source or fixture has diff --git a/tests/utils.ts b/tests/utils.ts index 9c0341d3..69b32aab 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -13,7 +13,15 @@ const fixturesPath = path.join(__dirname, 'fixtures'); export const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm'] as const; -export async function fixture( +/** + * Returns the contents of a file from the "tests/fixtures" directory. + * The "tests/fixtures" directory contains sub-directories, "scenarios". + * This is we can have different sets of fixtures, depending on what we're testing. + * + * The default scenario is "default", and represents the the file contents when we provide + * no arguments to the blueprint + */ +export async function readFixture( /** * Which file within in the fixture-set / scenario to read */ From aa1bb47756d6ddff1d5f0d0cae45e6b92725ab87 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Sun, 16 Jul 2023 10:39:55 -0400 Subject: [PATCH 09/10] setting up an addon-creating helper so we can more easily manage the creation of addons will need to propagate through the existing tests but that can happen in other PRs --- tests/assertions.ts | 3 +- tests/fixtures.ts | 81 ++++++++++++ tests/smoke-tests/--addon-only.test.ts | 28 ++--- .../custom-locations.test.ts | 5 +- .../within-existing-monorepo/defaults.test.ts | 4 +- tests/test-helpers.ts | 115 ++++++++++++++++++ tests/utils.ts | 37 ------ 7 files changed, 212 insertions(+), 61 deletions(-) create mode 100644 tests/fixtures.ts create mode 100644 tests/test-helpers.ts diff --git a/tests/assertions.ts b/tests/assertions.ts index 94783f4b..4c865f40 100644 --- a/tests/assertions.ts +++ b/tests/assertions.ts @@ -3,7 +3,8 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { expect } from 'vitest'; -import { packageJsonAt, readFixture } from './utils.js'; +import { readFixture } from './fixtures.js'; +import { packageJsonAt } from './utils.js'; interface AssertGeneratedOptions { projectRoot: string; diff --git a/tests/fixtures.ts b/tests/fixtures.ts new file mode 100644 index 00000000..cc814a07 --- /dev/null +++ b/tests/fixtures.ts @@ -0,0 +1,81 @@ +import fse from 'fs-extra'; +import assert from 'node:assert'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const fixturesPath = path.join(__dirname, 'fixtures'); + +/** + * Returns the contents of a file from the "tests/fixtures" directory. + * The "tests/fixtures" directory contains sub-directories, "scenarios". + * This is we can have different sets of fixtures, depending on what we're testing. + * + * The default scenario is "default", and represents the the file contents when we provide + * no arguments to the blueprint + */ +export async function readFixture( + /** + * Which file within in the fixture-set / scenario to read + */ + file: string, + options?: { + /** + * Which fixture set to use + */ + scenario?: string; + } +) { + let scenario = options?.scenario ?? 'default'; + let fixtureFilePath = path.isAbsolute(file) ? file : path.join(fixturesPath, scenario, file); + + let exists = await fse.pathExists(fixtureFilePath); + + assert( + exists, + `Fixture file '${file}' does not exist. To make this work, place a new file '${file}' in the 'tests/fixtures/${scenario}' directory. Checked the absolute path: '${fixtureFilePath}'.` + ); + + let contents = await fs.readFile(fixtureFilePath); + + return contents.toString(); +} + +export async function copyFixture( + /** + * Which file within the fixture-set / scenario to copy + */ + newFile: string, + options?: { + /** + * Which fixture set to use + */ + scenario?: string; + /** + * By default, the file used will be the same as the testFilePath, but + * in the fixtures directory under the (maybe) specified scenario. + * this can be overridden, if needed. + * (like if you're testFilePath is deep with in an existing monorepo, and wouldn't + * inherently match our default-project structure used in the fixtures) + */ + file?: string; + /** + * The working directory to use for the relative paths. Defaults to process.cwd() (node default) + */ + cwd?: string; + } +) { + let scenario = options?.scenario ?? 'default'; + let fixtureFile = options?.file ?? newFile; + + if (options?.cwd) { + newFile = path.join(options.cwd, newFile); + } + + let fixtureContents = await readFixture(fixtureFile, { scenario }); + + await fse.mkdir(path.dirname(newFile), { recursive: true }); + await fs.writeFile(newFile, fixtureContents); +} diff --git a/tests/smoke-tests/--addon-only.test.ts b/tests/smoke-tests/--addon-only.test.ts index 5605f17a..a96ff47f 100644 --- a/tests/smoke-tests/--addon-only.test.ts +++ b/tests/smoke-tests/--addon-only.test.ts @@ -1,34 +1,24 @@ import fse from 'fs-extra'; -import fs from 'node:fs/promises'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -import { createAddon, createTmp, install, runScript } from '../utils.js'; +import { AddonHelper } from '../test-helpers.js'; describe('--addon-only', () => { - let cwd = ''; - let tmpDir = ''; + let helper = new AddonHelper({ packageManager: 'pnpm', args: ['--addon-only'] }); beforeAll(async () => { - tmpDir = await createTmp(); - - let { name } = await createAddon({ - args: ['--addon-only', '--pnpm=true'], - options: { cwd: tmpDir }, - }); - - cwd = path.join(tmpDir, name); - - await install({ cwd, packageManager: 'pnpm' }); + await helper.setup(); + await helper.installDeps(); }); afterAll(async () => { - fs.rm(tmpDir, { recursive: true, force: true }); + await helper.clean(); }); it('is not a monorepo', async () => { - let hasPnpmWorkspace = await fse.pathExists(path.join(cwd, 'pnpm-workspace.yaml')); - let packageJson = await fse.readJson(path.join(cwd, 'package.json')); + let hasPnpmWorkspace = await fse.pathExists(path.join(helper.cwd, 'pnpm-workspace.yaml')); + let packageJson = await fse.readJson(path.join(helper.cwd, 'package.json')); expect(hasPnpmWorkspace).toBe(false); // Pnpm doesn't use this field, but it's good that it doesn't exist. @@ -36,13 +26,13 @@ describe('--addon-only', () => { }); it('can build', async () => { - let { exitCode } = await runScript({ cwd, script: 'build', packageManager: 'pnpm' }); + let { exitCode } = await helper.build(); expect(exitCode).toEqual(0); }); it('has passing lints', async () => { - let { exitCode } = await runScript({ cwd, script: 'lint', packageManager: 'pnpm' }); + let { exitCode } = await helper.build(); expect(exitCode).toEqual(0); }); diff --git a/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts b/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts index 09a99435..4a5becd1 100644 --- a/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts +++ b/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts @@ -4,10 +4,11 @@ import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { assertGeneratedCorrectly } from '../../assertions.js'; -import { createAddon, createTmp, fixture, install, runScript } from '../../utils.js'; +import { readFixture } from '../../fixtures.js'; +import { createAddon, createTmp, install, runScript } from '../../utils.js'; let commonFixtures = { - '.prettierrc.js': await fixture('.prettierrc.js'), + '.prettierrc.js': await readFixture('.prettierrc.js'), }; describe('custom locations', () => { diff --git a/tests/smoke-tests/within-existing-monorepo/defaults.test.ts b/tests/smoke-tests/within-existing-monorepo/defaults.test.ts index b543eef2..b507f088 100644 --- a/tests/smoke-tests/within-existing-monorepo/defaults.test.ts +++ b/tests/smoke-tests/within-existing-monorepo/defaults.test.ts @@ -4,17 +4,17 @@ import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { assertGeneratedCorrectly } from '../../assertions.js'; +import { readFixture } from '../../fixtures.js'; import { createAddon, createTmp, - fixture, install, runScript, SUPPORTED_PACKAGE_MANAGERS, } from '../../utils.js'; let commonFixtures = { - '.prettierrc.js': await fixture('.prettierrc.js'), + '.prettierrc.js': await readFixture('.prettierrc.js'), }; for (let packageManager of SUPPORTED_PACKAGE_MANAGERS) { diff --git a/tests/test-helpers.ts b/tests/test-helpers.ts new file mode 100644 index 00000000..3c59abb1 --- /dev/null +++ b/tests/test-helpers.ts @@ -0,0 +1,115 @@ +import assert from 'node:assert'; +import fs from 'node:fs/promises'; +import path from 'node:path'; + +import { matchesFixture } from './assertions.js'; +import { copyFixture } from './fixtures.js'; +import { createAddon, createTmp, install, runScript } from './utils.js'; + +const DEBUG = process.env.DEBUG === 'true'; + +/** + * Helps with common addon testing concerns. + * tl;dr: + * it's a wrapper around ember addon -b (so we can pass our flags with less duplication) + * it lets us set compare against a fixture set / scenario + * + * To DEBUG the intermediate output (in tmp), + * re-start your tests with `DEBUG=true`, and the tmpdir will be printed + * as well as the `clean` function will not run so that if a test finishes, + * you can still inspect the folder contents + * + */ +export class AddonHelper { + #cwd?: string; + #tmpDir?: string; + #scenario: string; + #packageManager: 'npm' | 'pnpm' | 'yarn'; + #args: string[]; + #fixtures: AddonFixtureHelper | undefined; + + constructor(options: { + args?: string[]; + scenario?: string; + packageManager: 'pnpm' | 'npm' | 'yarn'; + }) { + this.#args = options.args || []; + this.#scenario = options.scenario || 'default'; + this.#packageManager = options.packageManager; + } + + async setup() { + this.#tmpDir = await createTmp(); + + if (DEBUG) { + console.debug(`Debug test repo at ${this.#tmpDir}`); + } + + let { name } = await createAddon({ + args: this.#args, + options: { cwd: this.#tmpDir }, + }); + + // this is the project root + this.#cwd = path.join(this.#tmpDir, name); + + this.#fixtures = new AddonFixtureHelper({ cwd: this.#cwd, scenario: this.#scenario }); + } + + async run(scriptName: string) { + return await runScript({ + cwd: this.cwd, + script: scriptName, + packageManager: this.#packageManager, + }); + } + + async build() { + return this.run('build'); + } + + async clean() { + if (DEBUG) return; + + assert( + this.#tmpDir, + "Cannot clean without a tmpDir. Was the Addon Helper's `setup` method called to generate the addon?" + ); + + await fs.rm(this.#tmpDir, { recursive: true, force: true }); + } + + async installDeps() { + await install({ cwd: this.cwd, packageManager: this.#packageManager, skipPrepare: true }); + } + + get cwd() { + assert(this.#cwd, "Cannot get cwd. Was the Addon Helper's `setup` method called?"); + + return this.#cwd; + } + + get fixtures() { + assert(this.#fixtures, 'Cannot get fixtures-helper. Was the Addon Helper `setup`?'); + + return this.#fixtures; + } +} + +export class AddonFixtureHelper { + #cwd: string; + #scenario: string; + + constructor(options: { cwd: string; scenario?: string }) { + this.#cwd = options.cwd; + this.#scenario = options.scenario || 'default'; + } + + async use(file: string) { + await copyFixture(file, { scenario: this.#scenario, cwd: this.#cwd }); + } + + async matches(outputFile: string) { + await matchesFixture(outputFile, { scenario: this.#scenario, cwd: this.#cwd }); + } +} diff --git a/tests/utils.ts b/tests/utils.ts index 69b32aab..2067da71 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -1,6 +1,5 @@ import { type Options, execa } from 'execa'; import fse from 'fs-extra'; -import assert from 'node:assert'; import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; @@ -9,45 +8,9 @@ import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const blueprintPath = path.join(__dirname, '..'); -const fixturesPath = path.join(__dirname, 'fixtures'); export const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm'] as const; -/** - * Returns the contents of a file from the "tests/fixtures" directory. - * The "tests/fixtures" directory contains sub-directories, "scenarios". - * This is we can have different sets of fixtures, depending on what we're testing. - * - * The default scenario is "default", and represents the the file contents when we provide - * no arguments to the blueprint - */ -export async function readFixture( - /** - * Which file within in the fixture-set / scenario to read - */ - file: string, - options?: { - /** - * Which fixture set to use - */ - scenario?: string; - } -) { - let scenario = options?.scenario ?? 'default'; - let fixtureFilePath = path.isAbsolute(file) ? file : path.join(fixturesPath, scenario, file); - - let exists = await fse.pathExists(fixtureFilePath); - - assert( - exists, - `Fixture file '${file}' does not exist. To make this work, place a new file '${file}' in the 'tests/fixtures/${scenario}' directory. Checked the absolute path: '${fixtureFilePath}'.` - ); - - let contents = await fs.readFile(fixtureFilePath); - - return contents.toString(); -} - export async function createTmp() { let prefix = 'v2-addon-blueprint--'; let prefixPath = path.join(os.tmpdir(), prefix); From 5d2aae963d908c72435a08f8894354a49486e6f5 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 17 Jul 2023 08:23:39 -0400 Subject: [PATCH 10/10] Move the helpers in to a directory, so they aren't confused with top-level config --- tests/helpers.ts | 4 ++++ tests/{ => helpers}/assertions.ts | 0 tests/{ => helpers}/fixtures.ts | 2 +- tests/{test-helpers.ts => helpers/meta-helpers.ts} | 0 tests/{ => helpers}/utils.ts | 3 ++- tests/smoke-tests/--addon-location.test.ts | 9 +++++++-- tests/smoke-tests/--addon-only.test.ts | 2 +- tests/smoke-tests/--test-app-location.test.ts | 9 +++++++-- tests/smoke-tests/--typescript.test.ts | 4 ++-- tests/smoke-tests/defaults.test.ts | 4 ++-- .../within-existing-monorepo/custom-locations.test.ts | 11 ++++++++--- .../within-existing-monorepo/defaults.test.ts | 6 +++--- 12 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 tests/helpers.ts rename tests/{ => helpers}/assertions.ts (100%) rename tests/{ => helpers}/fixtures.ts (97%) rename tests/{test-helpers.ts => helpers/meta-helpers.ts} (100%) rename tests/{ => helpers}/utils.ts (98%) diff --git a/tests/helpers.ts b/tests/helpers.ts new file mode 100644 index 00000000..ae90a921 --- /dev/null +++ b/tests/helpers.ts @@ -0,0 +1,4 @@ +export * from './helpers/assertions.js'; +export * from './helpers/fixtures.js'; +export * from './helpers/meta-helpers.js'; +export * from './helpers/utils.js'; diff --git a/tests/assertions.ts b/tests/helpers/assertions.ts similarity index 100% rename from tests/assertions.ts rename to tests/helpers/assertions.ts diff --git a/tests/fixtures.ts b/tests/helpers/fixtures.ts similarity index 97% rename from tests/fixtures.ts rename to tests/helpers/fixtures.ts index cc814a07..7eb68b02 100644 --- a/tests/fixtures.ts +++ b/tests/helpers/fixtures.ts @@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const fixturesPath = path.join(__dirname, 'fixtures'); +const fixturesPath = path.join(__dirname, '../fixtures'); /** * Returns the contents of a file from the "tests/fixtures" directory. diff --git a/tests/test-helpers.ts b/tests/helpers/meta-helpers.ts similarity index 100% rename from tests/test-helpers.ts rename to tests/helpers/meta-helpers.ts diff --git a/tests/utils.ts b/tests/helpers/utils.ts similarity index 98% rename from tests/utils.ts rename to tests/helpers/utils.ts index 2067da71..cf62a6b8 100644 --- a/tests/utils.ts +++ b/tests/helpers/utils.ts @@ -7,7 +7,8 @@ import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const blueprintPath = path.join(__dirname, '..'); +// repo-root +const blueprintPath = path.join(__dirname, '../..'); export const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm'] as const; diff --git a/tests/smoke-tests/--addon-location.test.ts b/tests/smoke-tests/--addon-location.test.ts index fc181738..e380ba19 100644 --- a/tests/smoke-tests/--addon-location.test.ts +++ b/tests/smoke-tests/--addon-location.test.ts @@ -2,8 +2,13 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -import { assertGeneratedCorrectly } from '../assertions.js'; -import { createAddon, createTmp, install, runScript } from '../utils.js'; +import { + assertGeneratedCorrectly, + createAddon, + createTmp, + install, + runScript, +} from '../helpers.js'; describe('--addon-location', () => { let cwd = ''; diff --git a/tests/smoke-tests/--addon-only.test.ts b/tests/smoke-tests/--addon-only.test.ts index a96ff47f..c3a97766 100644 --- a/tests/smoke-tests/--addon-only.test.ts +++ b/tests/smoke-tests/--addon-only.test.ts @@ -2,7 +2,7 @@ import fse from 'fs-extra'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -import { AddonHelper } from '../test-helpers.js'; +import { AddonHelper } from '../helpers.js'; describe('--addon-only', () => { let helper = new AddonHelper({ packageManager: 'pnpm', args: ['--addon-only'] }); diff --git a/tests/smoke-tests/--test-app-location.test.ts b/tests/smoke-tests/--test-app-location.test.ts index e5bd1d0e..01e44915 100644 --- a/tests/smoke-tests/--test-app-location.test.ts +++ b/tests/smoke-tests/--test-app-location.test.ts @@ -2,8 +2,13 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -import { assertGeneratedCorrectly } from '../assertions.js'; -import { createAddon, createTmp, install, runScript } from '../utils.js'; +import { + assertGeneratedCorrectly, + createAddon, + createTmp, + install, + runScript, +} from '../helpers.js'; describe('--test-app-location', () => { let cwd = ''; diff --git a/tests/smoke-tests/--typescript.test.ts b/tests/smoke-tests/--typescript.test.ts index 57328eab..a9cb843e 100644 --- a/tests/smoke-tests/--typescript.test.ts +++ b/tests/smoke-tests/--typescript.test.ts @@ -2,15 +2,15 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -import { assertGeneratedCorrectly } from '../assertions.js'; import { + assertGeneratedCorrectly, createAddon, createTmp, dirContents, install, runScript, SUPPORTED_PACKAGE_MANAGERS, -} from '../utils.js'; +} from '../helpers.js'; for (let packageManager of SUPPORTED_PACKAGE_MANAGERS) { describe(`--typescript with ${packageManager}`, () => { diff --git a/tests/smoke-tests/defaults.test.ts b/tests/smoke-tests/defaults.test.ts index 063fc9ba..7ad458ca 100644 --- a/tests/smoke-tests/defaults.test.ts +++ b/tests/smoke-tests/defaults.test.ts @@ -3,15 +3,15 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -import { assertGeneratedCorrectly } from '../assertions.js'; import { + assertGeneratedCorrectly, createAddon, createTmp, dirContents, install, runScript, SUPPORTED_PACKAGE_MANAGERS, -} from '../utils.js'; +} from '../helpers.js'; for (let packageManager of SUPPORTED_PACKAGE_MANAGERS) { describe(`defaults with ${packageManager}`, () => { diff --git a/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts b/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts index 4a5becd1..06fbc184 100644 --- a/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts +++ b/tests/smoke-tests/within-existing-monorepo/custom-locations.test.ts @@ -3,9 +3,14 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -import { assertGeneratedCorrectly } from '../../assertions.js'; -import { readFixture } from '../../fixtures.js'; -import { createAddon, createTmp, install, runScript } from '../../utils.js'; +import { + assertGeneratedCorrectly, + createAddon, + createTmp, + install, + readFixture, + runScript, +} from '../../helpers.js'; let commonFixtures = { '.prettierrc.js': await readFixture('.prettierrc.js'), diff --git a/tests/smoke-tests/within-existing-monorepo/defaults.test.ts b/tests/smoke-tests/within-existing-monorepo/defaults.test.ts index b507f088..9f5ad44f 100644 --- a/tests/smoke-tests/within-existing-monorepo/defaults.test.ts +++ b/tests/smoke-tests/within-existing-monorepo/defaults.test.ts @@ -3,15 +3,15 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -import { assertGeneratedCorrectly } from '../../assertions.js'; -import { readFixture } from '../../fixtures.js'; import { + assertGeneratedCorrectly, createAddon, createTmp, install, + readFixture, runScript, SUPPORTED_PACKAGE_MANAGERS, -} from '../../utils.js'; +} from '../../helpers.js'; let commonFixtures = { '.prettierrc.js': await readFixture('.prettierrc.js'),