Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
setting up an addon-creating helper so we can more easily manage the …
Browse files Browse the repository at this point in the history
…creation of addons

will need to propagate through the existing tests
but that can happen in other PRs
NullVoxPopuli committed Jul 17, 2023

Verified

This commit was signed with the committer’s verified signature.
1 parent 6ea52bc commit e524cc5
Showing 6 changed files with 194 additions and 57 deletions.
81 changes: 81 additions & 0 deletions tests/fixtures.ts
Original file line number Diff line number Diff line change
@@ -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);
}
24 changes: 8 additions & 16 deletions tests/smoke-tests/--addon-only.test.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,26 @@
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';
import { runScript } from '../utils.js';

describe('--addon-only', () => {
let helper = new AddonHelper({ packageManager: 'pnpm', args: ['--addon-only'] });
let cwd = '';
let tmpDir = '';

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.
Original file line number Diff line number Diff line change
@@ -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', () => {
4 changes: 2 additions & 2 deletions tests/smoke-tests/within-existing-monorepo/defaults.test.ts
Original file line number Diff line number Diff line change
@@ -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) {
100 changes: 100 additions & 0 deletions tests/test-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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.
*
* 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[];

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.#cwd = path.join(this.#tmpDir, name);
}

async clean() {
if (DEBUG) return;

assert(
this.#tmpDir,
"Cannot clean without a tmpDir. Was the 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 Helper's `setup` method called?");

return this.#cwd;
}
}

export class AddonFixtureHelper {
#cwd: string;
#scenario: string;
#packageManager: 'npm' | 'pnpm' | 'yarn';

constructor(options: {
cwd: string;
scenario?: string;
packageManager: 'pnpm' | 'npm' | 'yarn';
}) {
this.#cwd = options.cwd;
this.#scenario = options.scenario || 'default';
this.#packageManager = options.packageManager;
}

async use(file: string) {
await copyFixture(file, { scenario: this.#scenario, cwd: this.#cwd });
}

async build() {
await runScript({ cwd: this.#cwd, script: 'build', packageManager: this.#packageManager });
}

async matches(outputFile: string) {
await matchesFixture(outputFile, { scenario: this.#scenario, cwd: this.#cwd });
}
}
37 changes: 0 additions & 37 deletions tests/utils.ts
Original file line number Diff line number Diff line change
@@ -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);

0 comments on commit e524cc5

Please sign in to comment.