diff --git a/cli/scripts/e2e/e2e.ts b/cli/scripts/e2e/e2e.ts index 76d2e82b5239f..089d14f8acc98 100644 --- a/cli/scripts/e2e/e2e.ts +++ b/cli/scripts/e2e/e2e.ts @@ -1,5 +1,5 @@ import execa from "execa"; -import { test } from "uvu"; +import * as uvu from "uvu"; import * as assert from "uvu/assert"; import { Monorepo } from "../monorepo"; import path from "path"; @@ -17,12 +17,15 @@ const basicPipeline = { outputs: ["dist/**"], }, }, + globalDependencies: ["$GLOBAL_ENV_DEPENDENCY"], }; // This is injected by github actions process.env.TURBO_TOKEN = ""; +let suites = []; for (let npmClient of ["yarn", "berry", "pnpm", "npm"] as const) { + const Suite = uvu.suite(`${npmClient}`); const repo = new Monorepo("basics"); repo.init(npmClient, basicPipeline); repo.install(); @@ -30,7 +33,7 @@ for (let npmClient of ["yarn", "berry", "pnpm", "npm"] as const) { repo.addPackage("b"); repo.addPackage("c"); repo.linkPackages(); - runSmokeTests(repo, npmClient); + runSmokeTests(Suite, repo, npmClient); const sub = new Monorepo("in-subdirectory"); sub.init(npmClient, basicPipeline, "js"); sub.install(); @@ -38,145 +41,178 @@ for (let npmClient of ["yarn", "berry", "pnpm", "npm"] as const) { sub.addPackage("b"); sub.addPackage("c"); sub.linkPackages(); - runSmokeTests(sub, npmClient, { + runSmokeTests(Suite, sub, npmClient, { cwd: path.join(sub.root, sub.subdir), }); + suites.push(Suite); // test that turbo can run from a subdirectory } -test.run(); +for (let s of suites) { + s.run(); +} -function runSmokeTests( +function runSmokeTests( + suite: uvu.Test, repo: Monorepo, npmClient: "yarn" | "berry" | "pnpm" | "npm", options: execa.SyncOptions = {} ) { - test(`${npmClient} runs tests and logs ${ - options.cwd ? " from " + options.cwd : "" - } `, async () => { - const results = repo.turbo("run", ["test", "--stream", "-vvv"], options); - const out = results.stdout + results.stderr; - assert.equal(0, results.exitCode); - const lines = out.split("\n"); - const chash = lines.find((l) => l.startsWith("c:test")); - assert.ok(!!chash, "No hash for c:test"); - const splitMessage = chash.split(" "); - const hash = splitMessage[splitMessage.length - 1]; - const logFilePath = path.join( - repo.subdir ? repo.subdir + "/" : "", - "node_modules", - ".cache", - "turbo", - hash, - ".turbo", - "turbo-test.log" - ); - let text = ""; - assert.not.throws(() => { - text = repo.readFileSync(logFilePath); - }, `Could not read log file from cache ${logFilePath}`); - - assert.ok(text.includes("testing c"), "Contains correct output"); - - repo.newBranch("my-feature-branch"); - repo.commitFiles({ - [path.join("packages", "a", "test.js")]: `console.log('testingz a');`, - }); - - const sinceResults = repo.turbo( - "run", - ["test", "--since=main", "--stream", "-vvv"], - options - ); - const testCLine = (sinceResults.stdout + sinceResults.stderr).split("\n"); - - assert.equal( - `• Packages changed since main: a`, - testCLine[0], - "Calculates changed packages (--since)" - ); - assert.equal(`• Packages in scope: a`, testCLine[1], "Packages in scope"); - assert.equal( - `• Running test in 1 packages`, - testCLine[2], - "Runs only in changed packages" - ); - assert.ok( - testCLine[3].startsWith(`a:test: cache miss, executing`), - "Cache miss in changed package" - ); - - // Check cache hit after another run - const since2Results = repo.turbo( - "run", - ["test", "--since=main", "--stream", "-vvv"], - options - ); - const testCLine2 = (since2Results.stdout + since2Results.stderr).split( - "\n" - ); - assert.equal( - `• Packages changed since main: a`, - testCLine2[0], - "Calculates changed packages (--since) after a second run" - ); - assert.equal( - `• Packages in scope: a`, - testCLine2[1], - "Packages in scope after a second run" - ); - assert.equal( - `• Running test in 1 packages`, - testCLine2[2], - "Runs only in changed packages after a second run" - ); - - assert.ok( - testCLine2[3].startsWith(`a:test: cache hit, replaying output`), - "Cache hit in changed package after a second run" - ); - - // Check that hashes are different and trigger a cascade - repo.commitFiles({ - [path.join("packages", "b", "test.js")]: `console.log('testingz b');`, - }); - - const hashChangeResults = repo.turbo("run", ["test", "--stream"], options); - const hashChangeResultsOut = - hashChangeResults.stdout + hashChangeResults.stderr; - console.log("------------------------------------------------------"); - console.log(hashChangeResultsOut); - console.log("------------------------------------------------------"); - const testCLine3 = hashChangeResultsOut.split("\n"); - - assert.equal( - `• Packages in scope: a, b, c`, - testCLine3[0], - "Packages in scope after a third run" - ); - assert.equal( - `• Running test in 3 packages`, - testCLine3[1], - "Runs correct number of packages" - ); - assert.ok( - testCLine3.findIndex((l) => - l.startsWith("a:test: cache miss, executing") - ) >= 0, - `A was impacted.` - ); - assert.ok( - testCLine3.findIndex((l) => - l.startsWith("b:test: cache miss, executing") - ) >= 0, - `B was impacted.` - ); - assert.ok( - testCLine3.findIndex((l) => - l.startsWith("c:test: cache hit, replaying output") - ) >= 0, - `C was unchanged` - ); + suite.after(() => { repo.cleanup(); }); + + suite( + `${npmClient} runs tests and logs ${ + options.cwd ? " from " + options.cwd : "" + } `, + async () => { + const results = repo.turbo("run", ["test", "--stream", "-vvv"], options); + assert.equal(0, results.exitCode, "exit code should be 0"); + const commandOutput = getCommandOutputAsArray(results); + const hash = getHashFromOutput(commandOutput, "c#test"); + assert.ok(!!hash, "No hash for c#test"); + const cachedLogFilePath = getCachedLogFilePathForTask( + getCachedDirForHash(repo, hash), + "test" + ); + let text = ""; + assert.not.throws(() => { + text = repo.readFileSync(cachedLogFilePath); + }, `Could not read cached log file from cache ${cachedLogFilePath}`); + assert.ok(text.includes("testing c"), "Contains correct output"); + } + ); + + suite( + `${npmClient} handles filesystem changes ${ + options.cwd ? " from " + options.cwd : "" + } `, + async () => { + repo.newBranch("my-feature-branch"); + repo.commitFiles({ + [path.join("packages", "a", "test.js")]: `console.log('testingz a');`, + }); + + const sinceCommandOutput = getCommandOutputAsArray( + repo.turbo("run", ["test", "--since=main", "--stream", "-vvv"], options) + ); + + assert.equal( + `• Packages changed since main: a`, + sinceCommandOutput[0], + "Calculates changed packages (--since)" + ); + assert.equal( + `• Packages in scope: a`, + sinceCommandOutput[1], + "Packages in scope" + ); + assert.equal( + `• Running test in 1 packages`, + sinceCommandOutput[2], + "Runs only in changed packages" + ); + assert.ok( + sinceCommandOutput[3].startsWith(`a:test: cache miss, executing`), + "Cache miss in changed package" + ); + + // Check cache hit after another run + const sinceCommandSecondRunOutput = getCommandOutputAsArray( + repo.turbo("run", ["test", "--since=main", "--stream", "-vvv"], options) + ); + assert.equal( + `• Packages changed since main: a`, + sinceCommandSecondRunOutput[0], + "Calculates changed packages (--since) after a second run" + ); + assert.equal( + `• Packages in scope: a`, + sinceCommandSecondRunOutput[1], + "Packages in scope after a second run" + ); + assert.equal( + `• Running test in 1 packages`, + sinceCommandSecondRunOutput[2], + "Runs only in changed packages after a second run" + ); + + assert.ok( + sinceCommandSecondRunOutput[3].startsWith( + `a:test: cache hit, replaying output` + ), + "Cache hit in changed package after a second run" + ); + + // Check that hashes are different and trigger a cascade + repo.commitFiles({ + [path.join("packages", "b", "test.js")]: `console.log('testingz b');`, + }); + + const commandOnceBHasChangedOutput = getCommandOutputAsArray( + repo.turbo("run", ["test", "--stream"], options) + ); + + assert.equal( + `• Packages in scope: a, b, c`, + commandOnceBHasChangedOutput[0], + "After running, changing source of b, and running `turbo run test` again, should print `Packages in scope: a, b, c`" + ); + assert.equal( + `• Running test in 3 packages`, + commandOnceBHasChangedOutput[1], + "After running, changing source of b, and running `turbo run test` again, should print `Running in 3 packages`" + ); + assert.ok( + commandOnceBHasChangedOutput.findIndex((l) => + l.startsWith("a:test: cache miss, executing") + ) >= 0, + "After running, changing source of b, and running `turbo run test` again, should print `a:test: cache miss, executing` since a depends on b and b has changed" + ); + assert.ok( + commandOnceBHasChangedOutput.findIndex((l) => + l.startsWith("b:test: cache miss, executing") + ) >= 0, + "After running, changing source of b, and running `turbo run test` again, should print `b:test: cache miss, executing` since b has changed" + ); + assert.ok( + commandOnceBHasChangedOutput.findIndex((l) => + l.startsWith("c:test: cache hit, replaying output") + ) >= 0, + "After running, changing source of b, and running `turbo run test` again, should print `c:test: cache hit, replaying output` since c should not be impacted by changes to b" + ); + } + ); +} + +function getCommandOutputAsArray( + results: execa.ExecaSyncReturnValue +): string[] { + return (results.stdout + results.stderr).split("\n"); +} + +function getHashFromOutput(lines: string[], taskId: string): string { + const normalizedTaskId = taskId.replace("#", ":"); + const line = lines.find((l) => l.startsWith(normalizedTaskId)); + const splitMessage = line.split(" "); + const hash = splitMessage[splitMessage.length - 1]; + return hash; +} + +function getCachedDirForHash(repo: Monorepo, hash: string): string { + return path.join( + repo.subdir ? repo.subdir + "/" : "", + "node_modules", + ".cache", + "turbo", + hash + ); +} + +function getCachedLogFilePathForTask( + cacheDir: string, + taskName: string +): string { + return path.join(cacheDir, ".turbo", `turbo-${taskName}.log`); } diff --git a/cli/scripts/package.json b/cli/scripts/package.json index 521cc7a08adcf..f45526ab47a49 100644 --- a/cli/scripts/package.json +++ b/cli/scripts/package.json @@ -13,7 +13,7 @@ "parse-package-name": "^0.1.0", "shelljs": "^0.8.4", "tempy": "^1.0.0", - "uvu": "^0.5.1", + "uvu": "^0.5.3", "watchlist": "^0.2.3" }, "scripts": { diff --git a/cli/scripts/yarn.lock b/cli/scripts/yarn.lock index f3e99f367de3e..e0acf54f9018f 100644 --- a/cli/scripts/yarn.lock +++ b/cli/scripts/yarn.lock @@ -1567,11 +1567,6 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -totalist@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-2.0.0.tgz#db6f1e19c0fa63e71339bbb8fba89653c18c7eec" - integrity sha512-+Y17F0YzxfACxTyjfhnJQEe7afPA0GSpYlFkl2VFMxYP7jshQf9gXV7cH47EfToBumFThfKBvfAcoUn6fdNeRQ== - type-fest@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" @@ -1622,16 +1617,15 @@ util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -uvu@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.1.tgz#938b85f96b8a478e585363ad1849933b6c481b28" - integrity sha512-JGxttnOGDFs77FaZ0yMUHIzczzQ5R1IlDeNW6Wymw6gAscwMdAffVOP6TlxLIfReZyK8tahoGwWZaTCJzNFDkg== +uvu@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.3.tgz#3d83c5bc1230f153451877bfc7f4aea2392219ae" + integrity sha512-brFwqA3FXzilmtnIyJ+CxdkInkY/i4ErvP7uV0DnUVxQcQ55reuHphorpF+tZoVHK2MniZ/VJzI7zJQoc9T9Yw== dependencies: dequal "^2.0.0" diff "^5.0.0" kleur "^4.0.3" sade "^1.7.3" - totalist "^2.0.0" watchlist@^0.2.3: version "0.2.3"