because I got bored of customising my CV for every job
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore(e2e-coverage): add c8 one-shot runner and external URL support

+123 -13
+9 -2
apps/server/package.json
··· 8 8 "start": "node dist/main.js", 9 9 "dev": "nodemon --watch src -e ts --exec \"ts-node -r tsconfig-paths/register src/main.ts\"", 10 10 "lint": "biome check .", 11 - "test": "jest", 11 + "test": "jest --config ./test/jest-unit.json", 12 + "test:unit": "jest --config ./test/jest-unit.json", 12 13 "test:e2e": "jest --config ./test/jest-e2e.json", 14 + "e2e:coverage:c8:start": "npm run prisma:deploy && npm run build && npx c8 -r text -r lcov node dist/main.js", 15 + "e2e:coverage:c8:report": "npx c8 report -r text -r lcov", 16 + "e2e:coverage:c8": "E2E_EXTERNAL_URL=http://localhost:3000 npm run test:e2e && npm run e2e:coverage:c8:report", 17 + "e2e:coverage:c8:stop": "pkill -f 'node dist/main.js' || true", 18 + "e2e:coverage:all": "node src/scripts/e2e-coverage-all.cjs", 13 19 "prisma:generate": "prisma generate --schema=./prisma/schema.prisma", 14 20 "prisma:migrate": "prisma migrate dev --schema=./prisma/schema.prisma", 15 21 "prisma:deploy": "prisma migrate deploy --schema=./prisma/schema.prisma", 16 22 "prisma:studio": "prisma studio --schema=./prisma/schema.prisma", 17 - "seed": "ts-node -r tsconfig-paths/register src/scripts/seed.ts" 23 + "seed": "ts-node -r tsconfig-paths/register src/scripts/seed.ts", 24 + "seed:test": "ts-node -r tsconfig-paths/register src/scripts/seed-test.ts" 18 25 }, 19 26 "dependencies": { 20 27 "@cv/utils": "^0.0.0",
+75
apps/server/src/scripts/e2e-coverage-all.cjs
··· 1 + /* eslint-disable no-console */ 2 + const { execSync, spawn } = require("node:child_process"); 3 + 4 + function sleep(ms) { 5 + return new Promise((resolve) => setTimeout(resolve, ms)); 6 + } 7 + 8 + async function main() { 9 + execSync("npm run prisma:deploy", { stdio: "inherit" }); 10 + execSync("npm run build", { stdio: "inherit" }); 11 + 12 + const fs = require("node:fs"); 13 + const path = require("node:path"); 14 + function findMain() { 15 + const direct = path.join(process.cwd(), "dist", "main.js"); 16 + if (fs.existsSync(direct)) return direct; 17 + const candidates = [ 18 + path.join(process.cwd(), "dist", "src", "main.js"), 19 + path.join(process.cwd(), "dist", "apps", "server", "src", "main.js"), 20 + ]; 21 + for (const c of candidates) if (fs.existsSync(c)) return c; 22 + return null; 23 + } 24 + 25 + let mainPath = findMain(); 26 + let retries = 10; 27 + while (!mainPath && retries > 0) { 28 + await sleep(300); 29 + retries--; 30 + mainPath = findMain(); 31 + } 32 + if (!mainPath) { 33 + throw new Error("dist/main.js not found after build; aborting c8 run"); 34 + } 35 + 36 + const server = spawn( 37 + "npx", 38 + [ 39 + "c8", 40 + "--include", 41 + "dist/**/*.js", 42 + "--exclude", 43 + "dist/**/test/**", 44 + "-r", 45 + "text", 46 + "-r", 47 + "lcov", 48 + "node", 49 + mainPath, 50 + ], 51 + { stdio: "inherit" }, 52 + ); 53 + 54 + await sleep(1500); 55 + 56 + execSync("npm run seed:test", { stdio: "inherit" }); 57 + 58 + const env = { ...process.env, E2E_EXTERNAL_URL: "http://localhost:3000" }; 59 + execSync("npm run test:e2e -- --maxWorkers=50%", { stdio: "inherit", env }); 60 + 61 + try { 62 + server.kill("SIGTERM"); 63 + } catch {} 64 + await sleep(1000); 65 + execSync( 66 + "npx c8 report --include 'dist/**/*.js' --exclude 'dist/**/test/**' -r text -r lcov", 67 + { stdio: "inherit", shell: "/bin/sh" }, 68 + ); 69 + } 70 + 71 + main().catch(() => { 72 + process.exit(1); 73 + }); 74 + 75 +
+39 -11
apps/server/test/test-utils.ts
··· 21 21 * Sets up a test application with proper configuration 22 22 */ 23 23 export async function setupTestApp(): Promise<INestApplication> { 24 + // When targeting an external running server (c8 coverage mode), 25 + // skip spinning up an in-memory Nest application. 26 + if (process.env["E2E_EXTERNAL_URL"]) { 27 + return { 28 + // Provide a no-op close to satisfy tests calling app.close() 29 + close: async () => undefined, 30 + } as unknown as INestApplication; 31 + } 24 32 // Set test environment variables 25 33 process.env.JWT_SECRET = "test-secret-key-for-testing-only"; 26 34 process.env.PORT = "3001"; ··· 57 65 password: "password123", 58 66 }; 59 67 60 - const registerResponse = await request(app.getHttpServer()).post("/graphql").send({ 68 + const registerResponse = await getRequester(app).post("/graphql").send({ 61 69 query: REGISTER_MUTATION, 62 70 variables: testUser, 63 71 }); 64 72 65 - if (registerResponse.status !== 200 || !registerResponse.body.data?.register) { 66 - throw new Error(`Registration failed: ${JSON.stringify(registerResponse.body)}`); 73 + if ( 74 + registerResponse.status !== 200 || 75 + !registerResponse.body.data?.register 76 + ) { 77 + throw new Error( 78 + `Registration failed: ${JSON.stringify(registerResponse.body)}`, 79 + ); 67 80 } 68 81 69 82 return { ··· 84 97 skillId: string; 85 98 }> { 86 99 const timestamp = Date.now(); 87 - 100 + 88 101 // Create company 89 102 const createCompanyMutation = ` 90 103 mutation CreateCompany($name: String!, $description: String) { ··· 96 109 } 97 110 `; 98 111 99 - const companyResponse = await request(app.getHttpServer()) 112 + const companyResponse = await getRequester(app) 100 113 .post("/graphql") 101 114 .send({ 102 115 query: createCompanyMutation, ··· 120 133 } 121 134 `; 122 135 123 - const roleResponse = await request(app.getHttpServer()) 136 + const roleResponse = await getRequester(app) 124 137 .post("/graphql") 125 138 .send({ 126 139 query: createRoleMutation, ··· 144 157 } 145 158 `; 146 159 147 - const levelResponse = await request(app.getHttpServer()) 160 + const levelResponse = await getRequester(app) 148 161 .post("/graphql") 149 162 .send({ 150 163 query: createLevelMutation, ··· 168 181 } 169 182 `; 170 183 171 - const skillResponse = await request(app.getHttpServer()) 184 + const skillResponse = await getRequester(app) 172 185 .post("/graphql") 173 186 .send({ 174 187 query: createSkillMutation, ··· 187 200 /** 188 201 * Makes an authenticated GraphQL request 189 202 */ 190 - export function makeAuthenticatedRequest(app: INestApplication, accessToken: string) { 191 - return request(app.getHttpServer()) 203 + export function makeAuthenticatedRequest( 204 + app: INestApplication, 205 + accessToken: string, 206 + ) { 207 + return getRequester(app) 192 208 .post("/graphql") 193 209 .set("Authorization", `Bearer ${accessToken}`); 194 210 } ··· 197 213 * Makes an unauthenticated GraphQL request 198 214 */ 199 215 export function makeUnauthenticatedRequest(app: INestApplication) { 200 - return request(app.getHttpServer()).post("/graphql"); 216 + return getRequester(app).post("/graphql"); 217 + } 218 + 219 + /** 220 + * Returns a supertest requester. If E2E_EXTERNAL_URL is set, target that URL 221 + * so we can run the app under c8 separately and still reuse the test suites. 222 + */ 223 + function getRequester(app: INestApplication) { 224 + const externalUrl = process.env["E2E_EXTERNAL_URL"]; 225 + if (externalUrl) { 226 + return request(externalUrl); 227 + } 228 + return request(app.getHttpServer()); 201 229 }