Skip to content
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
9 changes: 9 additions & 0 deletions .changeset/tired-places-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@halfdomelabs/project-builder-server': patch
'@halfdomelabs/project-builder-cli': patch
'@halfdomelabs/project-builder-lib': patch
'@halfdomelabs/project-builder-web': patch
'@halfdomelabs/create-project': patch
---

Introduce E2E testing for project-builder-cli
32 changes: 19 additions & 13 deletions .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ jobs:
test:
name: Test E2E
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.51.0-noble
options: --user 1001

steps:
- name: Checkout Repo
Expand All @@ -33,18 +36,21 @@ jobs:
node-version-file: package.json
cache: 'pnpm'
- name: Install Dependencies
run: pnpm install --frozen-lockfile --filter @halfdomelabs/project-builder-test... --filter @halfdomelabs/root
run: pnpm install --frozen-lockfile --filter @halfdomelabs/project-builder-test... --filter @halfdomelabs/project-builder-cli... --filter @halfdomelabs/root

# Install Playwright Browsers
- name: Get Playwright version
id: get-playwright-version
run: echo "PLAYWRIGHT_VERSION=$(jq '.devDependencies["@playwright/test"]' package.json -r)" >> $GITHUB_OUTPUT
working-directory: tests/simple/packages/e2e
- name: Cache Playwright Browsers
id: cache-playwright-browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ runner.os }}-${{ steps.get-playwright-version.outputs.PLAYWRIGHT_VERSION }}

- run: pnpm test:e2e:affected
# - name: Get Playwright version
# id: get-playwright-version
# run: echo "PLAYWRIGHT_VERSION=$(jq '.devDependencies["@playwright/test"]' package.json -r)" >> $GITHUB_OUTPUT
# working-directory: packages/project-builder-cli
# - name: Cache Playwright Browsers
# id: cache-playwright-browsers
# uses: actions/cache@v4
# with:
# path: ~/.cache/ms-playwright
# key: playwright-browsers-${{ runner.os }}-${{ steps.get-playwright-version.outputs.PLAYWRIGHT_VERSION }}
# - name: Install Playwright
# run: pnpm exec playwright install
# working-directory: packages/project-builder-cli
- name: Run E2E Tests
run: pnpm test:e2e:affected
9 changes: 6 additions & 3 deletions knip.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,17 @@
]
},
"packages/project-builder-cli": {
"entry": ["src/index.{ts,tsx}"],
"project": "src/**/*.{ts,tsx}",
"entry": ["src/index.{ts,tsx}", "tests/**/*.spec.{ts,tsx}"],
"project": ["src/**/*.{ts,tsx}", "tests/**/*.{ts,tsx}"],
"ignoreDependencies": [
// pino-pretty is referenced by string (https://github.com/pinojs/pino/blob/ad864b7ae02b314b9a548614f705a437e0db78c3/docs/transports.md)
"pino-pretty",
// we resolve the package by string in src/server.ts
"@halfdomelabs/project-builder-web"
]
],
"paths": {
"@src/*": ["./src/*"]
}
},
"packages/project-builder-test": {
"entry": ["src/index.{ts,tsx}"],
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"serve": "turbo run @halfdomelabs/project-builder-cli:start serve",
"test": "turbo run test --continue",
"test:affected": "turbo run test --affected --continue",
"test:e2e": "turbo run @halfdomelabs/project-builder-test#test:e2e",
"test:e2e:affected": "turbo run @halfdomelabs/project-builder-test#test:e2e --affected",
"test:e2e": "turbo run test:e2e --continue",
"test:e2e:affected": "turbo run test:e2e --affected --continue",
"test:generate": "pnpm run --filter @halfdomelabs/project-builder-test start generate",
"typecheck": "turbo run typecheck --affected",
"versions:check": "check-dependency-version-consistency .",
Expand Down
1 change: 0 additions & 1 deletion packages/create-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"ora": "^8.0.1"
},
"devDependencies": {
"@halfdomelabs/project-builder-lib": "workspace:*",
"@halfdomelabs/tools": "workspace:*",
"@types/node": "catalog:",
"eslint": "catalog:",
Expand Down
16 changes: 0 additions & 16 deletions packages/create-project/src/project-creator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { ProjectDefinitionInput } from '@halfdomelabs/project-builder-lib';

import chalk from 'chalk';
import { execa } from 'execa';
import fs from 'node:fs/promises';
Expand Down Expand Up @@ -79,20 +77,6 @@ export async function generateBaseplateProject({
),
);

await writeFile(
'baseplate/project-definition.json',
JSON.stringify(
{
name: packageName,
features: [],
models: [],
portOffset: 3000,
} satisfies ProjectDefinitionInput,
null,
2,
),
);

await copyFile('.gitignore', '.gitignore');
await copyFile('.pnpmfile.cjs', '.pnpmfile.cjs');
await copyFile('.template.npmrc', '.template.npmrc');
Expand Down
8 changes: 7 additions & 1 deletion packages/project-builder-cli/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
lib
dist
node_modules
node_modules

# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
7 changes: 5 additions & 2 deletions packages/project-builder-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@
"scripts/**/*"
],
"scripts": {
"build": "tsc && tsc-alias",
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
"clean": "rm -rf ./dist",
"dev": "tsx watch --exclude /**/node_modules/** -r dotenv/config ./src",
"dev:serve": "pnpm dev serve",
"lint": "eslint .",
"prettier:check": "prettier --check .",
"prettier:write": "prettier -w .",
"start": "tsx -r dotenv/config ./src",
"typecheck": "tsc --noEmit"
"test:e2e": "pnpm playwright test",
"typecheck": "tsc --build tsconfig.json"
},
"dependencies": {
"@halfdomelabs/project-builder-common": "workspace:*",
Expand All @@ -45,9 +46,11 @@
},
"devDependencies": {
"@halfdomelabs/tools": "workspace:*",
"@playwright/test": "1.51.0",
"@types/node": "catalog:",
"dotenv": "^16.0.3",
"eslint": "catalog:",
"fastify": "5.1.0",
"prettier": "catalog:",
"tsc-alias": "catalog:",
"tsx": "catalog:",
Expand Down
36 changes: 36 additions & 0 deletions packages/project-builder-cli/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { defineConfig, devices } from '@playwright/test';

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'line',

/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},

{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},

{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
6 changes: 1 addition & 5 deletions packages/project-builder-cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ async function createSchemaParserContext(
async function runMain(): Promise<void> {
const version = await getPackageVersion();

if (!version) {
throw new Error('Could not determine package version');
}

program.version(version, '-v, --version');

program
Expand All @@ -45,7 +41,7 @@ async function runMain(): Promise<void> {
});
});

addServeCommand(program, version);
addServeCommand(program);

await program.parseAsync(process.argv);
}
Expand Down
80 changes: 54 additions & 26 deletions packages/project-builder-cli/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,72 @@
import type { Command } from 'commander';
import type { FastifyInstance } from 'fastify';
import type { Logger } from 'pino';

import { getDefaultPlugins } from '@halfdomelabs/project-builder-common';
import { startWebServer } from '@halfdomelabs/project-builder-server';
import {
BuilderServiceManager,
startWebServer,
} from '@halfdomelabs/project-builder-server';
import path from 'node:path';
import { packageDirectory } from 'pkg-dir';

import { getEnabledFeatureFlags } from './services/feature-flags.js';
import { logger } from './services/logger.js';
import { expandPathWithTilde } from './utils/path.js';
import { resolveModule } from './utils/resolve.js';
import { getPackageVersion } from './utils/version.js';

interface ServeCommandOptions {
browser: boolean;
port: number;
logger?: Logger;
}

export function addServeCommand(program: Command, version: string): void {
export async function serveWebServer(
directories: string[],
{ browser, port, logger: overrideLogger }: ServeCommandOptions,
): Promise<{
fastifyInstance: FastifyInstance;
serviceManager: BuilderServiceManager;
}> {
const projectBuilderWebDir = await packageDirectory({
cwd: resolveModule('@halfdomelabs/project-builder-web/package.json'),
});
const resolvedDirectories = directories.map((dir) =>
expandPathWithTilde(dir),
);
const builtInPlugins = await getDefaultPlugins(logger);
const version = await getPackageVersion();

if (!projectBuilderWebDir) {
throw new Error(
`Unable to find project-builder-web package to host website`,
);
}

const serviceManager = new BuilderServiceManager({
initialDirectories: resolvedDirectories,
cliVersion: version,
builtInPlugins,
});

const fastifyInstance = await startWebServer({
serviceManager,
browser,
port,
cliVersion: version,
projectBuilderStaticDir: path.join(projectBuilderWebDir, 'dist'),
logger: overrideLogger ?? logger,
featureFlags: getEnabledFeatureFlags(),
});

return {
fastifyInstance,
serviceManager,
};
}

export function addServeCommand(program: Command): void {
program
.command('serve')
.description('Starts the project builder web service')
Expand All @@ -38,30 +89,7 @@ export function addServeCommand(program: Command, version: string): void {
)
.action(
async (directories: string[], { browser, port }: ServeCommandOptions) => {
const projectBuilderWebDir = await packageDirectory({
cwd: resolveModule('@halfdomelabs/project-builder-web/package.json'),
});
const resolvedDirectories = directories.map((dir) =>
expandPathWithTilde(dir),
);
const builtInPlugins = await getDefaultPlugins(logger);

if (!projectBuilderWebDir) {
throw new Error(
`Unable to find project-builder-web package to host website`,
);
}

return startWebServer({
directories: resolvedDirectories,
browser,
port,
cliVersion: version,
projectBuilderStaticDir: path.join(projectBuilderWebDir, 'dist'),
logger,
builtInPlugins,
featureFlags: getEnabledFeatureFlags(),
});
await serveWebServer(directories, { browser, port });
},
);
}
6 changes: 4 additions & 2 deletions packages/project-builder-cli/src/services/logger.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { pino } from 'pino';

export const logger = pino({
export const DEFAULT_LOGGER_OPTIONS: pino.LoggerOptions = {
transport: {
target: 'pino-pretty',
options: {
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
},
});
};

export const logger = pino(DEFAULT_LOGGER_OPTIONS);
16 changes: 11 additions & 5 deletions packages/project-builder-cli/src/utils/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { findNearestPackageJson } from '@halfdomelabs/utils/node';
import { promises as fs } from 'node:fs';
import { fileURLToPath } from 'node:url';

let cachedVersion: string | undefined | null;
let cachedVersion: string | undefined;

export async function getPackageVersion(): Promise<string | null> {
export async function getPackageVersion(): Promise<string> {
if (cachedVersion === undefined) {
// Construct the path to the package.json file.
const packageJsonPath = await findNearestPackageJson({
Expand All @@ -14,12 +14,18 @@ export async function getPackageVersion(): Promise<string | null> {
if (packageJsonPath) {
// Read the package.json file.
const fileContent = await fs.readFile(packageJsonPath, 'utf8');
const packageJson = JSON.parse(fileContent) as { version: string };
const packageJson = JSON.parse(fileContent) as {
version: string | undefined;
};

if (!packageJson.version) {
throw new Error('Unable to find version in package.json');
}

// Return the version.
cachedVersion = packageJson.version || null;
cachedVersion = packageJson.version;
} else {
cachedVersion = null;
throw new Error('Unable to find package.json');
}
}

Expand Down
Loading