diff options
| author | 2023-04-28 01:36:44 +0800 | |
|---|---|---|
| committer | 2023-04-28 01:36:44 +0800 | |
| commit | dd84b9d64fb98746a230cd24233ff50a562c39c9 (patch) | |
| tree | b583261ef00b3afe72ec4d6dacb31e57779a6faf /packages/turbo-codemod/__tests__ | |
| parent | 0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff) | |
| download | HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip | |
Diffstat (limited to 'packages/turbo-codemod/__tests__')
69 files changed, 4203 insertions, 0 deletions
diff --git a/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/has-package-manager/package.json b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/has-package-manager/package.json new file mode 100644 index 0000000..d6edac5 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/has-package-manager/package.json @@ -0,0 +1,7 @@ +{ + "name": "has-package-manager", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/no-package-manager/package.json b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/no-package-manager/package.json new file mode 100644 index 0000000..2e28fe4 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/no-package-manager/package.json @@ -0,0 +1,6 @@ +{ + "name": "no-package-manager", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {} +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/wrong-package-manager/package.json b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/wrong-package-manager/package.json new file mode 100644 index 0000000..f58aca2 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/add-package-manager/wrong-package-manager/package.json @@ -0,0 +1,7 @@ +{ + "name": "has-package-manager", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "turbo@1.7.0" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/package.json new file mode 100644 index 0000000..c4606fa --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/package.json @@ -0,0 +1,28 @@ +{ + "name": "both-configs", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3", + "turbo": { + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "package-only": { + "cache": false, + "persistent": true + }, + "build": { + "outputs": [ + ".next/**", + "!.next/cache/**" + ] + }, + "lint": { + "outputs": [] + }, + "dev": { + "cache": false + } + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/turbo.json new file mode 100644 index 0000000..e6eb652 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/both-configs/turbo.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "turbo-only": { + "cache": false, + "persistent": true + }, + "build": { + "outputs": [".next/**", "!.next/cache/**"] + }, + "lint": { + "outputs": [] + }, + "dev": { + "cache": false + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-config/package.json new file mode 100644 index 0000000..b965b7d --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-config/package.json @@ -0,0 +1,7 @@ +{ + "name": "no-turbo-json-config", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-file/a-random-file.txt b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-file/a-random-file.txt new file mode 100644 index 0000000..7488fec --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-package-json-file/a-random-file.txt @@ -0,0 +1 @@ +Nothing exists here diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-turbo-json-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-turbo-json-config/package.json new file mode 100644 index 0000000..7754c7d --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/no-turbo-json-config/package.json @@ -0,0 +1,24 @@ +{ + "name": "no-turbo-json-config", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3", + "turbo": { + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build": { + "outputs": [ + ".next/**", + "!.next/cache/**" + ] + }, + "lint": { + "outputs": [] + }, + "dev": { + "cache": false + } + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/package.json new file mode 100644 index 0000000..a48d0ec --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/package.json @@ -0,0 +1,7 @@ +{ + "name": "both-configs", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/turbo.json new file mode 100644 index 0000000..e6eb652 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/create-turbo-config/turbo-json-config/turbo.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "turbo-only": { + "cache": false, + "persistent": true + }, + "build": { + "outputs": [".next/**", "!.next/cache/**"] + }, + "lint": { + "outputs": [] + }, + "dev": { + "cache": false + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-deps/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-deps/package.json new file mode 100644 index 0000000..b632eef --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-deps/package.json @@ -0,0 +1,4 @@ +{ + "name": "no-turbo", + "version": "0.0.0" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-package/README.md b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-package/README.md new file mode 100644 index 0000000..64355e7 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-package/README.md @@ -0,0 +1 @@ +Nothing here diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-turbo/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-turbo/package.json new file mode 100644 index 0000000..524df50 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/no-turbo/package.json @@ -0,0 +1,6 @@ +{ + "name": "no-turbo", + "version": "0.0.0", + "dependencies": {}, + "devDependencies": {} +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces-dev-install/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces-dev-install/package.json new file mode 100644 index 0000000..f5b2368 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces-dev-install/package.json @@ -0,0 +1,12 @@ +{ + "name": "normal-workspaces", + "version": "0.0.0", + "workspaces": [ + "apps/*", + "packages/*" + ], + "dependencies": {}, + "devDependencies": { + "turbo": "1.0.0" + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces/package.json new file mode 100644 index 0000000..6344a38 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/normal-workspaces/package.json @@ -0,0 +1,12 @@ +{ + "name": "normal-workspaces", + "version": "0.0.0", + "workspaces": [ + "apps/*", + "packages/*" + ], + "dependencies": { + "turbo": "1.0.0" + }, + "devDependencies": {} +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/package.json new file mode 100644 index 0000000..5c12f28 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/package.json @@ -0,0 +1,8 @@ +{ + "name": "pnpm-workspaces", + "version": "0.0.0", + "dependencies": {}, + "devDependencies": { + "turbo": "1.0.0" + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/pnpm-workspace.yaml b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/pnpm-workspace.yaml new file mode 100644 index 0000000..3ff5faa --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces-dev-install/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - "apps/*" + - "packages/*" diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/package.json new file mode 100644 index 0000000..fedeb8d --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/package.json @@ -0,0 +1,8 @@ +{ + "name": "pnpm-workspaces", + "version": "0.0.0", + "dependencies": { + "turbo": "1.0.0" + }, + "devDependencies": {} +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/pnpm-workspace.yaml b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/pnpm-workspace.yaml new file mode 100644 index 0000000..3ff5faa --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/pnpm-workspaces/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - "apps/*" + - "packages/*" diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package-dev-install/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package-dev-install/package.json new file mode 100644 index 0000000..38bd995 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package-dev-install/package.json @@ -0,0 +1,8 @@ +{ + "name": "single-package-dev-install", + "version": "0.0.0", + "dependencies": {}, + "devDependencies": { + "turbo": "1.0.0" + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package/package.json b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package/package.json new file mode 100644 index 0000000..0fd3453 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/get-turbo-upgrade-command/single-package/package.json @@ -0,0 +1,8 @@ +{ + "name": "single-package", + "version": "0.0.0", + "dependencies": { + "turbo": "1.0.0" + }, + "devDependencies": {} +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/env-dependencies/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/env-dependencies/turbo.json new file mode 100644 index 0000000..bb3e248 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/env-dependencies/turbo.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": ["$NEXT_PUBLIC_API_KEY", "$STRIPE_API_KEY", ".env"], + "pipeline": { + "build": { + "outputs": [".next/**", "!.next/cache/**"], + "dependsOn": ["^build", "$PROD_API_KEY"] + }, + "lint": { + "outputs": [], + "dependsOn": ["$IS_CI"] + }, + "test": { + "outputs": [], + "dependsOn": ["$IS_CI", "test"] + }, + "dev": { + "cache": false + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/migrated-env-dependencies/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/migrated-env-dependencies/turbo.json new file mode 100644 index 0000000..9217af6 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/migrated-env-dependencies/turbo.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": [], + "globalEnv": ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"], + "pipeline": { + "build": { + "dependsOn": ["^build"], + "env": ["PROD_API_KEY"], + "outputs": [".next/**", "!.next/cache/**"] + }, + "dev": { + "cache": false + }, + "lint": { + "dependsOn": [], + "env": ["IS_CI"], + "outputs": [] + }, + "test": { + "dependsOn": ["test"], + "env": ["IS_CI"], + "outputs": [] + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/no-turbo-json/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/no-turbo-json/package.json new file mode 100644 index 0000000..83443be --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/no-turbo-json/package.json @@ -0,0 +1,7 @@ +{ + "name": "no-turbo-json", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/package.json new file mode 100644 index 0000000..6774d3c --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/package.json @@ -0,0 +1,20 @@ +{ + "name": "migrate-env-var-dependencies-old-config", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3", + "turbo": { + "pipeline": { + "build-one": { + "outputs": [ + "foo" + ] + }, + "build-two": { + "outputs": [] + }, + "build-three": {} + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/turbo.json new file mode 100644 index 0000000..b0f6150 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/old-config/turbo.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build-one": { + "outputs": ["foo"] + }, + "build-two": { + "outputs": [] + }, + "build-three": {} + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/index.js b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/index.js new file mode 100644 index 0000000..4de53f5 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/index.js @@ -0,0 +1,6 @@ +export default function docs() { + if (process.env.ENV_1 === undefined) { + return "does not exist"; + } + return "exists"; +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/package.json new file mode 100644 index 0000000..82f9a44 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/package.json @@ -0,0 +1,4 @@ +{ + "name": "docs", + "version": "1.0.0" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/turbo.json new file mode 100644 index 0000000..a3713ef --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/docs/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "pipeline": { + "build": { + "env": ["ENV_3"] + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/index.js b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/index.js new file mode 100644 index 0000000..bfd3ab8 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/index.js @@ -0,0 +1,6 @@ +export default function web() { + if (!process.env.ENV_2) { + return "bar"; + } + return "foo"; +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/package.json new file mode 100644 index 0000000..d8a83ed --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/package.json @@ -0,0 +1,4 @@ +{ + "name": "web", + "version": "1.0.0" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/turbo.json new file mode 100644 index 0000000..dd69c31 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/apps/web/turbo.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "pipeline": { + "build": { + // old + "dependsOn": ["build", "$ENV_2"], + // new + "env": ["ENV_1"] + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/package.json new file mode 100644 index 0000000..c6616a6 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/package.json @@ -0,0 +1,14 @@ +{ + "private": true, + "workspaces": [ + "apps/*", + "packages/*" + ], + "scripts": { + "build": "turbo run build" + }, + "devDependencies": { + "turbo": "latest" + }, + "packageManager": "yarn@1.22.19" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/index.js b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/index.js new file mode 100644 index 0000000..dee5e80 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/index.js @@ -0,0 +1,6 @@ +export default function foo() { + if (!process.env.IS_SERVER) { + return "bar"; + } + return "foo"; +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/package.json new file mode 100644 index 0000000..7cb7cf1 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/package.json @@ -0,0 +1,4 @@ +{ + "name": "ui", + "version": "1.0.0" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/turbo.json new file mode 100644 index 0000000..6ce7b30 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/packages/ui/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "pipeline": { + "build": { + "dependsOn": ["$IS_SERVER"] + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/turbo.json new file mode 100644 index 0000000..718e461 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate-env-var-dependencies/workspace-configs/turbo.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": ["$NEXT_PUBLIC_API_KEY", "$STRIPE_API_KEY", ".env"], + "pipeline": { + "build": { + "outputs": [".next/**", "!.next/cache/**"], + "dependsOn": ["^build", "$PROD_API_KEY"] + }, + "lint": { + "outputs": [], + "dependsOn": ["$IS_TEST"] + }, + "test": { + "outputs": [], + "dependsOn": ["$IS_CI", "test"] + }, + "dev": { + "cache": false + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate/no-repo/README.md b/packages/turbo-codemod/__tests__/__fixtures__/migrate/no-repo/README.md new file mode 100644 index 0000000..64355e7 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate/no-repo/README.md @@ -0,0 +1 @@ +Nothing here diff --git a/packages/turbo-codemod/__tests__/__fixtures__/migrate/old-turbo/package.json b/packages/turbo-codemod/__tests__/__fixtures__/migrate/old-turbo/package.json new file mode 100644 index 0000000..62959b8 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/migrate/old-turbo/package.json @@ -0,0 +1,26 @@ +{ + "name": "no-turbo-json", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": { + "turbo": "1.0.0" + }, + "turbo": { + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build": { + "outputs": [ + ".next/**", + "!.next/cache/**" + ] + }, + "lint": { + "outputs": [] + }, + "test": {}, + "dev": { + "cache": false + } + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/package.json new file mode 100644 index 0000000..6b50aac --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/package.json @@ -0,0 +1,7 @@ +{ + "name": "invalid-outputs", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/turbo.json new file mode 100644 index 0000000..33c2b93 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/invalid-outputs/turbo.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build-one": { + "outputs": ["foo"] + }, + "build-two": { + "outputs": [] + }, + "build-three": {}, + "garbage-in-numeric-0": { + "outputs": 0 + }, + "garbage-in-numeric": { + "outputs": 42 + }, + "garbage-in-string": { + "outputs": "string" + }, + "garbage-in-empty-string": { + "outputs": "" + }, + "garbage-in-null": { + "outputs": null + }, + "garbage-in-false": { + "outputs": false + }, + "garbage-in-true": { + "outputs": true + }, + "garbage-in-object": { + "outputs": {} + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/package.json new file mode 100644 index 0000000..4e17dc1 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/package.json @@ -0,0 +1,7 @@ +{ + "name": "no-outputs", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/turbo.json new file mode 100644 index 0000000..f5d57fc --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-outputs/turbo.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build-one": { + "dependsOn": ["build-two"] + }, + "build-two": { + "cache": false + }, + "build-three": { + "persistent": true + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/package.json new file mode 100644 index 0000000..6e20fc8 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/package.json @@ -0,0 +1,7 @@ +{ + "name": "no-pipeline", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/turbo.json new file mode 100644 index 0000000..0e2d6fd --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-pipeline/turbo.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": ["$NEXT_PUBLIC_API_KEY", "$STRIPE_API_KEY", ".env"], + "pipeline": {} +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-turbo-json/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-turbo-json/package.json new file mode 100644 index 0000000..cd98334 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/no-turbo-json/package.json @@ -0,0 +1,7 @@ +{ + "name": "set-default-outputs-no-turbo-json", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/package.json new file mode 100644 index 0000000..4c816c2 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/package.json @@ -0,0 +1,20 @@ +{ + "name": "set-default-outputs-old-config", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3", + "turbo": { + "pipeline": { + "build-one": { + "outputs": [ + "foo" + ] + }, + "build-two": { + "outputs": [] + }, + "build-three": {} + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/turbo.json new file mode 100644 index 0000000..b0f6150 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-config/turbo.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build-one": { + "outputs": ["foo"] + }, + "build-two": { + "outputs": [] + }, + "build-three": {} + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/package.json new file mode 100644 index 0000000..e4220ba --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/package.json @@ -0,0 +1,7 @@ +{ + "name": "old-outputs", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": {}, + "packageManager": "npm@1.2.3" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/turbo.json new file mode 100644 index 0000000..b0f6150 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/old-outputs/turbo.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build-one": { + "outputs": ["foo"] + }, + "build-two": { + "outputs": [] + }, + "build-three": {} + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/index.js b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/index.js new file mode 100644 index 0000000..4de53f5 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/index.js @@ -0,0 +1,6 @@ +export default function docs() { + if (process.env.ENV_1 === undefined) { + return "does not exist"; + } + return "exists"; +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/package.json new file mode 100644 index 0000000..82f9a44 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/package.json @@ -0,0 +1,4 @@ +{ + "name": "docs", + "version": "1.0.0" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/turbo.json new file mode 100644 index 0000000..e60cdb7 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/docs/turbo.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "pipeline": { + "build": {} + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/index.js b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/index.js new file mode 100644 index 0000000..bfd3ab8 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/index.js @@ -0,0 +1,6 @@ +export default function web() { + if (!process.env.ENV_2) { + return "bar"; + } + return "foo"; +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/package.json new file mode 100644 index 0000000..d8a83ed --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/package.json @@ -0,0 +1,4 @@ +{ + "name": "web", + "version": "1.0.0" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/turbo.json new file mode 100644 index 0000000..b239cbf --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/apps/web/turbo.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "pipeline": { + "build": { + // old + "outputs": [] + } + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/package.json new file mode 100644 index 0000000..c6616a6 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/package.json @@ -0,0 +1,14 @@ +{ + "private": true, + "workspaces": [ + "apps/*", + "packages/*" + ], + "scripts": { + "build": "turbo run build" + }, + "devDependencies": { + "turbo": "latest" + }, + "packageManager": "yarn@1.22.19" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/index.js b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/index.js new file mode 100644 index 0000000..dee5e80 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/index.js @@ -0,0 +1,6 @@ +export default function foo() { + if (!process.env.IS_SERVER) { + return "bar"; + } + return "foo"; +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/package.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/package.json new file mode 100644 index 0000000..7cb7cf1 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/package.json @@ -0,0 +1,4 @@ +{ + "name": "ui", + "version": "1.0.0" +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/turbo.json new file mode 100644 index 0000000..fe51119 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/packages/ui/turbo.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "pipeline": { + "build-three": {} + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/turbo.json b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/turbo.json new file mode 100644 index 0000000..b0f6150 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/set-default-outputs/workspace-configs/turbo.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build-one": { + "outputs": ["foo"] + }, + "build-two": { + "outputs": [] + }, + "build-three": {} + } +} diff --git a/packages/turbo-codemod/__tests__/__fixtures__/transform/basic/package.json b/packages/turbo-codemod/__tests__/__fixtures__/transform/basic/package.json new file mode 100644 index 0000000..651edb6 --- /dev/null +++ b/packages/turbo-codemod/__tests__/__fixtures__/transform/basic/package.json @@ -0,0 +1,8 @@ +{ + "name": "transform-basic", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": { + "turbo": "1.0.0" + } +} diff --git a/packages/turbo-codemod/__tests__/add-package-manager.test.ts b/packages/turbo-codemod/__tests__/add-package-manager.test.ts new file mode 100644 index 0000000..5bde7e0 --- /dev/null +++ b/packages/turbo-codemod/__tests__/add-package-manager.test.ts @@ -0,0 +1,504 @@ +import { transformer } from "../src/transforms/add-package-manager"; +import { setupTestFixtures } from "@turbo/test-utils"; +import fs from "fs-extra"; +import * as getPackageManager from "../src/utils/getPackageManager"; +import * as getPackageManagerVersion from "../src/utils/getPackageManagerVersion"; + +describe("add-package-manager", () => { + const { useFixture } = setupTestFixtures({ + directory: __dirname, + test: "add-package-manager", + }); + test("no package manager - basic", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + + const mockGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( + packageManager, + root + ); + + // package manager should now exist + expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( + `${packageManager}@${packageManagerVersion}` + ); + // result should be correct + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "modified", + "additions": 1, + "deletions": 0, + }, + } + `); + + mockGetPackageManagerVersion.mockRestore(); + mockGetPackageManager.mockRestore(); + }); + + test("no package manager - repeat run", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + + const mockGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + expect(mockGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( + packageManager, + root + ); + + // package manager should now exist + expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( + `${packageManager}@${packageManagerVersion}` + ); + // result should be correct + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "modified", + "additions": 1, + "deletions": 0, + }, + } + `); + + // run the transformer again to ensure nothing changes on a second run + const repeatResult = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + expect(repeatResult.fatalError).toBeUndefined(); + expect(repeatResult.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + } + `); + + mockGetPackageManagerVersion.mockRestore(); + mockGetPackageManager.mockRestore(); + }); + + test("no package manager - dry", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const packageManager = "npm"; + const packageManagerVersion = "1.2.3"; + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: true, print: false }, + }); + + expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( + packageManager, + root + ); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // result should be correct + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "skipped", + "additions": 1, + "deletions": 0, + }, + } + `); + + mockGetPackageManagerVersion.mockRestore(); + }); + + test("no package manager - print", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const packageManager = "yarn"; + const packageManagerVersion = "1.2.3"; + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + + const mockGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: true }, + }); + + expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( + packageManager, + root + ); + // package manager should now exist + expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( + `${packageManager}@${packageManagerVersion}` + ); + // result should be correct + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "modified", + "additions": 1, + "deletions": 0, + }, + } + `); + + mockGetPackageManagerVersion.mockRestore(); + }); + + test("no package manager - dry & print", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const packageManager = "npm"; + const packageManagerVersion = "1.2.3"; + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + + const mockGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: true, print: true }, + }); + + expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( + packageManager, + root + ); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // result should be correct + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "skipped", + "additions": 1, + "deletions": 0, + }, + } + `); + + mockGetPackageManagerVersion.mockRestore(); + mockGetPackageManager.mockRestore(); + }); + + test("package manager already exists", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "has-package-manager" }); + const packageManager = "npm"; + const packageManagerVersion = "1.2.3"; + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + + const mockGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + // package manager should exist + expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( + `${packageManager}@${packageManagerVersion}` + ); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( + packageManager, + root + ); + + // package manager should still exist + expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( + `${packageManager}@${packageManagerVersion}` + ); + // result should be correct + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + } + `); + + mockGetPackageManagerVersion.mockRestore(); + mockGetPackageManager.mockRestore(); + }); + + test("package manager exists but is wrong", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "wrong-package-manager" }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + + const mockGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + // package manager should exist + expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( + "turbo@1.7.0" + ); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( + packageManager, + root + ); + + // package manager should still exist + expect(JSON.parse(read("package.json") || "{}").packageManager).toBe( + `${packageManager}@${packageManagerVersion}` + ); + // result should be correct + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "modified", + "additions": 1, + "deletions": 1, + }, + } + `); + + mockGetPackageManagerVersion.mockRestore(); + mockGetPackageManager.mockRestore(); + }); + + test("errors when unable to determine workspace manager", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const mockGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(undefined); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(mockGetPackageManager).toHaveBeenCalledTimes(1); + expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + + // result should be correct + // result should be correct + expect(result.fatalError?.message).toMatch( + /Unable to determine package manager for .*?/ + ); + + mockGetPackageManager.mockRestore(); + }); + + test("errors when unable to determine package manager", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const mockGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockImplementation(() => { + throw new Error("package manager not supported"); + }); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(mockGetPackageManagerVersion).toHaveBeenCalledTimes(1); + + // result should be correct + expect(result.fatalError?.message).toMatch( + /Unable to determine package manager version for .*?/ + ); + + mockGetPackageManagerVersion.mockRestore(); + }); + + test("errors when unable to write json", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-manager" }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // mock out workspace and version detection so we're not dependent on our actual repo + const mockGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + + const mockGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + const mockWriteJsonSync = jest + .spyOn(fs, "writeJsonSync") + .mockImplementation(() => { + throw new Error("could not write file"); + }); + + // package manager should not exist + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(mockGetPackageManager).toHaveBeenCalledWith({ directory: root }); + expect(mockGetPackageManagerVersion).toHaveBeenCalledWith( + packageManager, + root + ); + + // package manager should still not exist (we couldn't write it) + expect( + JSON.parse(read("package.json") || "{}").packageManager + ).toBeUndefined(); + + // result should be correct + expect(result.fatalError?.message).toMatch( + "Encountered an error while transforming files" + ); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "error", + "additions": 1, + "deletions": 0, + "error": [Error: could not write file], + }, + } + `); + + mockWriteJsonSync.mockRestore(); + mockGetPackageManagerVersion.mockRestore(); + mockGetPackageManager.mockRestore(); + }); +}); diff --git a/packages/turbo-codemod/__tests__/create-turbo-config.test.ts b/packages/turbo-codemod/__tests__/create-turbo-config.test.ts new file mode 100644 index 0000000..8938c78 --- /dev/null +++ b/packages/turbo-codemod/__tests__/create-turbo-config.test.ts @@ -0,0 +1,416 @@ +import { transformer } from "../src/transforms/create-turbo-config"; +import { setupTestFixtures } from "@turbo/test-utils"; +import fs from "fs-extra"; + +describe("create-turbo-config", () => { + const { useFixture } = setupTestFixtures({ + directory: __dirname, + test: "create-turbo-config", + }); + + test("package.json config exists but no turbo.json config - basic", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-turbo-json-config" }); + + // turbo.json should not exist + expect(read("turbo.json")).toBeUndefined(); + + // get config from package.json for comparison later + const turboConfig = JSON.parse(read("package.json") || "{}").turbo; + expect(turboConfig).toBeDefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + // turbo.json should now exist (and match the package.json config) + expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboConfig); + + // result should be correct + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "modified", + "additions": 0, + "deletions": 1, + }, + "turbo.json": Object { + "action": "modified", + "additions": 1, + "deletions": 0, + }, + } + `); + }); + + test("package.json config exists but no turbo.json config - repeat run", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-turbo-json-config" }); + + // turbo.json should not exist + expect(read("turbo.json")).toBeUndefined(); + + // get config from package.json for comparison later + const turboConfig = JSON.parse(read("package.json") || "{}").turbo; + expect(turboConfig).toBeDefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + // turbo.json should now exist (and match the package.json config) + expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboConfig); + + // result should be correct + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "modified", + "additions": 0, + "deletions": 1, + }, + "turbo.json": Object { + "action": "modified", + "additions": 1, + "deletions": 0, + }, + } + `); + + // run the transformer + const repeatResult = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + // result should be correct + expect(repeatResult.fatalError).toBeUndefined(); + expect(repeatResult.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + "turbo.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + } + `); + }); + + test("package.json config exists but no turbo.json config - dry", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-turbo-json-config" }); + + // turbo.json should not exist + expect(read("turbo.json")).toBeUndefined(); + + // get config from package.json for comparison later + const turboConfig = JSON.parse(read("package.json") || "{}").turbo; + expect(turboConfig).toBeDefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: true, print: false }, + }); + + // turbo.json still not exist (dry run) + expect(read("turbo.json")).toBeUndefined(); + + // result should be correct + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "skipped", + "additions": 0, + "deletions": 1, + }, + "turbo.json": Object { + "action": "skipped", + "additions": 1, + "deletions": 0, + }, + } + `); + }); + + test("package.json config exists but no turbo.json config - print", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-turbo-json-config" }); + + // turbo.json should not exist + expect(read("turbo.json")).toBeUndefined(); + + // get config from package.json for comparison later + const turboConfig = JSON.parse(read("package.json") || "{}").turbo; + expect(turboConfig).toBeDefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: true }, + }); + + // turbo.json should now exist (and match the package.json config) + expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboConfig); + + // result should be correct + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "modified", + "additions": 0, + "deletions": 1, + }, + "turbo.json": Object { + "action": "modified", + "additions": 1, + "deletions": 0, + }, + } + `); + }); + + test("package.json config exists but no turbo.json config - dry & print", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-turbo-json-config" }); + + // turbo.json should not exist + expect(read("turbo.json")).toBeUndefined(); + + // get config from package.json for comparison later + const turboConfig = JSON.parse(read("package.json") || "{}").turbo; + expect(turboConfig).toBeDefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: true, print: true }, + }); + + // turbo.json still not exist (dry run) + expect(read("turbo.json")).toBeUndefined(); + + // result should be correct + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "skipped", + "additions": 0, + "deletions": 1, + }, + "turbo.json": Object { + "action": "skipped", + "additions": 1, + "deletions": 0, + }, + } + `); + }); + + test("no package.json config or turbo.json file exists", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-json-config" }); + + // turbo.json should not exist + expect(read("turbo.json")).toBeUndefined(); + + // get config from package.json for comparison later + const packageJsonConfig = JSON.parse(read("package.json") || "{}"); + const turboConfig = packageJsonConfig.turbo; + expect(turboConfig).toBeUndefined(); + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + // turbo.json should still not exist + expect(read("turbo.json")).toBeUndefined(); + + // make sure we didn't change the package.json + expect(JSON.parse(read("package.json") || "{}")).toEqual(packageJsonConfig); + + // result should be correct + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + "turbo.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + } + `); + }); + + test("no package.json file exists", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-package-json-file" }); + + // turbo.json should not exist + expect(read("turbo.json")).toBeUndefined(); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + // turbo.json should still not exist + expect(read("turbo.json")).toBeUndefined(); + + // result should be correct + expect(result.fatalError?.message).toMatch( + /No package\.json found at .*?\. Is the path correct\?/ + ); + }); + + test("turbo.json file exists and no package.json config exists", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "turbo-json-config" }); + + // turbo.json should exist + expect(read("turbo.json")).toBeDefined(); + + // no config should exist in package.json + const packageJsonConfig = JSON.parse(read("package.json") || "{}"); + const turboConfig = packageJsonConfig.turbo; + expect(turboConfig).toBeUndefined(); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + // turbo.json should still exist + expect(read("turbo.json")).toBeDefined(); + + // make sure we didn't change the package.json + expect(JSON.parse(read("package.json") || "{}")).toEqual(packageJsonConfig); + + // result should be correct + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + "turbo.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + } + `); + }); + + test("turbo.json file exists and package.json config exists", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "both-configs" }); + + // turbo.json should exist + const turboJsonConfig = JSON.parse(read("turbo.json") || "{}"); + expect(turboJsonConfig.pipeline).toBeDefined(); + + // no config should exist in package.json + const packageJsonConfig = JSON.parse(read("package.json") || "{}"); + const turboConfig = JSON.parse(read("package.json") || "{}").turbo; + expect(turboConfig).toBeDefined(); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + // make sure we didn't change the package.json + expect(JSON.parse(read("package.json") || "{}")).toEqual(packageJsonConfig); + + // make sure we didn't change the turbo.json + expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJsonConfig); + + // result should be correct + expect(result.fatalError?.message).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + "turbo.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + } + `); + }); + + test("errors when unable to write json", () => { + // load the fixture for the test + const { root, read } = useFixture({ fixture: "no-turbo-json-config" }); + + // turbo.json should not exist + expect(read("turbo.json")).toBeUndefined(); + + // get config from package.json for comparison later + const turboConfig = JSON.parse(read("package.json") || "{}").turbo; + expect(turboConfig).toBeDefined(); + + const mockWriteJsonSync = jest + .spyOn(fs, "writeJsonSync") + .mockImplementation(() => { + throw new Error("could not write file"); + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + // turbo.json should still not exist (error writing) + expect(read("turbo.json")).toBeUndefined(); + + // result should be correct + expect(result.fatalError).toBeDefined(); + expect(result.fatalError?.message).toMatch( + "Encountered an error while transforming files" + ); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "package.json": Object { + "action": "error", + "additions": 0, + "deletions": 1, + "error": [Error: could not write file], + }, + "turbo.json": Object { + "action": "error", + "additions": 1, + "deletions": 0, + "error": [Error: could not write file], + }, + } + `); + + mockWriteJsonSync.mockRestore(); + }); +}); diff --git a/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts b/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts new file mode 100644 index 0000000..1015589 --- /dev/null +++ b/packages/turbo-codemod/__tests__/get-turbo-upgrade-command.test.ts @@ -0,0 +1,576 @@ +import { setupTestFixtures } from "@turbo/test-utils"; +import getTurboUpgradeCommand from "../src/commands/migrate/steps/getTurboUpgradeCommand"; +import * as utils from "../src/commands/migrate/utils"; +import * as getPackageManager from "../src/utils/getPackageManager"; +import * as getPackageManagerVersion from "../src/utils/getPackageManagerVersion"; + +const LOCAL_INSTALL_COMMANDS = [ + // npm - workspaces + [ + "latest", + "npm", + "7.0.0", + "normal-workspaces-dev-install", + "npm install turbo@latest --save-dev", + ], + [ + "1.6.3", + "npm", + "7.0.0", + "normal-workspaces-dev-install", + "npm install turbo@1.6.3 --save-dev", + ], + [ + "canary", + "npm", + "7.0.0", + "normal-workspaces-dev-install", + "npm install turbo@canary --save-dev", + ], + ["latest", "npm", "7.0.0", "normal-workspaces", "npm install turbo@latest"], + // npm - single package + [ + "latest", + "npm", + "7.0.0", + "single-package-dev-install", + "npm install turbo@latest --save-dev", + ], + ["latest", "npm", "7.0.0", "single-package", "npm install turbo@latest"], + // pnpm - workspaces + [ + "latest", + "pnpm", + "7.0.0", + "pnpm-workspaces-dev-install", + "pnpm install turbo@latest --save-dev -w", + ], + [ + "1.6.3", + "pnpm", + "7.0.0", + "pnpm-workspaces-dev-install", + "pnpm install turbo@1.6.3 --save-dev -w", + ], + [ + "canary", + "pnpm", + "7.0.0", + "pnpm-workspaces-dev-install", + "pnpm install turbo@canary --save-dev -w", + ], + [ + "latest", + "pnpm", + "7.0.0", + "pnpm-workspaces", + "pnpm install turbo@latest -w", + ], + // pnpm - single package + [ + "latest", + "pnpm", + "7.0.0", + "single-package-dev-install", + "pnpm install turbo@latest --save-dev", + ], + ["latest", "pnpm", "7.0.0", "single-package", "pnpm install turbo@latest"], + // yarn 1.x - workspaces + [ + "latest", + "yarn", + "1.22.19", + "normal-workspaces-dev-install", + "yarn add turbo@latest --dev -W", + ], + [ + "latest", + "yarn", + "1.22.19", + "normal-workspaces", + "yarn add turbo@latest -W", + ], + [ + "1.6.3", + "yarn", + "1.22.19", + "normal-workspaces-dev-install", + "yarn add turbo@1.6.3 --dev -W", + ], + [ + "canary", + "yarn", + "1.22.19", + "normal-workspaces-dev-install", + "yarn add turbo@canary --dev -W", + ], + // yarn 1.x - single package + [ + "latest", + "yarn", + "1.22.19", + "single-package-dev-install", + "yarn add turbo@latest --dev", + ], + ["latest", "yarn", "1.22.19", "single-package", "yarn add turbo@latest"], + // yarn 2.x - workspaces + [ + "latest", + "yarn", + "2.3.4", + "normal-workspaces-dev-install", + "yarn add turbo@latest --dev", + ], + ["latest", "yarn", "2.3.4", "normal-workspaces", "yarn add turbo@latest"], + [ + "1.6.3", + "yarn", + "2.3.4", + "normal-workspaces-dev-install", + "yarn add turbo@1.6.3 --dev", + ], + [ + "canary", + "yarn", + "2.3.4", + "normal-workspaces-dev-install", + "yarn add turbo@canary --dev", + ], + // yarn 2.x - single package + [ + "latest", + "yarn", + "2.3.4", + "single-package-dev-install", + "yarn add turbo@latest --dev", + ], + ["latest", "yarn", "2.3.4", "single-package", "yarn add turbo@latest"], + // yarn 3.x - workspaces + [ + "latest", + "yarn", + "3.3.4", + "normal-workspaces-dev-install", + "yarn add turbo@latest --dev", + ], + ["latest", "yarn", "3.3.4", "normal-workspaces", "yarn add turbo@latest"], + [ + "1.6.3", + "yarn", + "3.3.4", + "normal-workspaces-dev-install", + "yarn add turbo@1.6.3 --dev", + ], + [ + "canary", + "yarn", + "3.3.4", + "normal-workspaces-dev-install", + "yarn add turbo@canary --dev", + ], + // yarn 3.x - single package + [ + "latest", + "yarn", + "3.3.4", + "single-package-dev-install", + "yarn add turbo@latest --dev", + ], + ["latest", "yarn", "3.3.4", "single-package", "yarn add turbo@latest"], +]; + +const GLOBAL_INSTALL_COMMANDS = [ + // npm + [ + "latest", + "npm", + "7.0.0", + "normal-workspaces-dev-install", + "npm install turbo@latest --global", + ], + [ + "1.6.3", + "npm", + "7.0.0", + "normal-workspaces-dev-install", + "npm install turbo@1.6.3 --global", + ], + [ + "latest", + "npm", + "7.0.0", + "normal-workspaces", + "npm install turbo@latest --global", + ], + [ + "latest", + "npm", + "7.0.0", + "single-package", + "npm install turbo@latest --global", + ], + [ + "latest", + "npm", + "7.0.0", + "single-package-dev-install", + "npm install turbo@latest --global", + ], + // pnpm + [ + "latest", + "pnpm", + "7.0.0", + "pnpm-workspaces-dev-install", + "pnpm install turbo@latest --global", + ], + [ + "1.6.3", + "pnpm", + "7.0.0", + "pnpm-workspaces-dev-install", + "pnpm install turbo@1.6.3 --global", + ], + [ + "latest", + "pnpm", + "7.0.0", + "pnpm-workspaces", + "pnpm install turbo@latest --global", + ], + [ + "latest", + "pnpm", + "7.0.0", + "single-package", + "pnpm install turbo@latest --global", + ], + [ + "latest", + "pnpm", + "7.0.0", + "single-package-dev-install", + "pnpm install turbo@latest --global", + ], + // yarn 1.x + [ + "latest", + "yarn", + "1.22.19", + "normal-workspaces-dev-install", + "yarn global add turbo@latest", + ], + [ + "latest", + "yarn", + "1.22.19", + "normal-workspaces", + "yarn global add turbo@latest", + ], + [ + "1.6.3", + "yarn", + "1.22.19", + "normal-workspaces-dev-install", + "yarn global add turbo@1.6.3", + ], + [ + "latest", + "yarn", + "1.22.19", + "single-package", + "yarn global add turbo@latest", + ], + [ + "latest", + "yarn", + "1.22.19", + "single-package-dev-install", + "yarn global add turbo@latest", + ], + // yarn 2.x + [ + "latest", + "yarn", + "2.3.4", + "normal-workspaces-dev-install", + "yarn global add turbo@latest", + ], + [ + "latest", + "yarn", + "2.3.4", + "normal-workspaces", + "yarn global add turbo@latest", + ], + [ + "1.6.3", + "yarn", + "2.3.4", + "normal-workspaces-dev-install", + "yarn global add turbo@1.6.3", + ], + ["latest", "yarn", "2.3.4", "single-package", "yarn global add turbo@latest"], + [ + "latest", + "yarn", + "2.3.4", + "single-package-dev-install", + "yarn global add turbo@latest", + ], + // yarn 3.x + [ + "latest", + "yarn", + "3.3.3", + "normal-workspaces-dev-install", + "yarn global add turbo@latest", + ], + [ + "latest", + "yarn", + "3.3.3", + "normal-workspaces", + "yarn global add turbo@latest", + ], + [ + "1.6.3", + "yarn", + "3.3.3", + "normal-workspaces-dev-install", + "yarn global add turbo@1.6.3", + ], + ["latest", "yarn", "3.3.4", "single-package", "yarn global add turbo@latest"], + [ + "latest", + "yarn", + "3.3.4", + "single-package-dev-install", + "yarn global add turbo@latest", + ], +]; + +describe("get-turbo-upgrade-command", () => { + const { useFixture } = setupTestFixtures({ + directory: __dirname, + test: "get-turbo-upgrade-command", + }); + + test.each(LOCAL_INSTALL_COMMANDS)( + "returns correct upgrade command for local install of turbo@%s using %s@%s (fixture: %s)", + ( + turboVersion, + packageManager, + packageManagerVersion, + fixture, + expectedUpgradeCommand + ) => { + const { root } = useFixture({ + fixture, + }); + + const mockedExec = jest + .spyOn(utils, "exec") + .mockImplementation((command: string) => { + // fail the check for the turbo, and package manager bins to force local + if (command.includes("bin")) { + return undefined; + } + }); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager as getPackageManager.PackageManager); + + // get the command + const upgradeCommand = getTurboUpgradeCommand({ + directory: root, + to: turboVersion === "latest" ? undefined : turboVersion, + }); + + expect(upgradeCommand).toEqual(expectedUpgradeCommand); + + mockedExec.mockRestore(); + mockedGetPackageManager.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + } + ); + + test.each(GLOBAL_INSTALL_COMMANDS)( + "returns correct upgrade command for global install of turbo@%s using %s@%s (fixture: %s)", + ( + turboVersion, + packageManager, + packageManagerVersion, + fixture, + expectedUpgradeCommand + ) => { + const { root } = useFixture({ + fixture, + }); + + const mockedExec = jest + .spyOn(utils, "exec") + .mockImplementation((command: string) => { + if (command === "turbo bin") { + return `/global/${packageManager}/bin/turbo`; + } + if (command.includes(packageManager)) { + return `/global/${packageManager}/bin`; + } + }); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager as getPackageManager.PackageManager); + + // get the command + const upgradeCommand = getTurboUpgradeCommand({ + directory: root, + to: turboVersion === "latest" ? undefined : turboVersion, + }); + + expect(upgradeCommand).toEqual(expectedUpgradeCommand); + + mockedExec.mockRestore(); + mockedGetPackageManager.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + } + ); + + test("fails gracefully if no package.json exists", () => { + const { root } = useFixture({ + fixture: "no-package", + }); + + const mockedExec = jest + .spyOn(utils, "exec") + .mockImplementation((command: string) => { + // fail the check for the turbo, and package manager bins to force local + if (command.includes("bin")) { + return undefined; + } + }); + + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue("8.0.0"); + const mockedGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue("pnpm" as getPackageManager.PackageManager); + + // get the command + const upgradeCommand = getTurboUpgradeCommand({ + directory: root, + }); + + expect(upgradeCommand).toEqual(undefined); + + mockedExec.mockRestore(); + mockedGetPackageManager.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + }); + + test("fails gracefully if turbo cannot be found in package.json", () => { + const { root } = useFixture({ + fixture: "no-turbo", + }); + + const mockedExec = jest + .spyOn(utils, "exec") + .mockImplementation((command: string) => { + // fail the check for the turbo, and package manager bins to force local + if (command.includes("bin")) { + return undefined; + } + }); + + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue("8.0.0"); + const mockedGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue("pnpm" as getPackageManager.PackageManager); + + // get the command + const upgradeCommand = getTurboUpgradeCommand({ + directory: root, + }); + + expect(upgradeCommand).toEqual(undefined); + + mockedExec.mockRestore(); + mockedGetPackageManager.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + }); + + test("fails gracefully if package.json has no deps or devDeps", () => { + const { root } = useFixture({ + fixture: "no-deps", + }); + + const mockedExec = jest + .spyOn(utils, "exec") + .mockImplementation((command: string) => { + // fail the check for the turbo, and package manager bins to force local + if (command.includes("bin")) { + return undefined; + } + }); + + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue("8.0.0"); + const mockedGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue("pnpm" as getPackageManager.PackageManager); + + // get the command + const upgradeCommand = getTurboUpgradeCommand({ + directory: root, + }); + + expect(upgradeCommand).toEqual(undefined); + + mockedExec.mockRestore(); + mockedGetPackageManager.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + }); + + test("fails gracefully if can't find packageManager", () => { + const { root } = useFixture({ + fixture: "no-deps", + }); + + const mockedExec = jest + .spyOn(utils, "exec") + .mockImplementation((command: string) => { + // fail the check for the turbo, and package manager bins to force local + if (command.includes("bin")) { + return undefined; + } + }); + + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue("8.0.0"); + const mockedGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue("pnpm" as getPackageManager.PackageManager); + + // get the command + const upgradeCommand = getTurboUpgradeCommand({ + directory: root, + }); + + expect(upgradeCommand).toEqual(undefined); + + mockedExec.mockRestore(); + mockedGetPackageManager.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + }); +}); diff --git a/packages/turbo-codemod/__tests__/migrate-env-var-dependencies.test.ts b/packages/turbo-codemod/__tests__/migrate-env-var-dependencies.test.ts new file mode 100644 index 0000000..fbc5d8d --- /dev/null +++ b/packages/turbo-codemod/__tests__/migrate-env-var-dependencies.test.ts @@ -0,0 +1,758 @@ +import merge from "deepmerge"; +import { + hasLegacyEnvVarDependencies, + migratePipeline, + migrateConfig, + transformer, +} from "../src/transforms/migrate-env-var-dependencies"; +import { setupTestFixtures } from "@turbo/test-utils"; +import type { Schema } from "@turbo/types"; + +const getTestTurboConfig = (override: Schema = { pipeline: {} }): Schema => { + const config = { + $schema: "./docs/public/schema.json", + globalDependencies: ["$GLOBAL_ENV_KEY"], + pipeline: { + test: { + outputs: ["coverage/**/*"], + dependsOn: ["^build"], + }, + lint: { + outputs: [], + }, + dev: { + cache: false, + }, + build: { + outputs: ["dist/**/*", ".next/**/*", "!.next/cache/**"], + dependsOn: ["^build", "$TASK_ENV_KEY", "$ANOTHER_ENV_KEY"], + }, + }, + }; + + return merge(config, override, { + arrayMerge: (_, sourceArray) => sourceArray, + }); +}; + +describe("migrate-env-var-dependencies", () => { + describe("hasLegacyEnvVarDependencies - utility", () => { + it("finds env keys in legacy turbo.json - has keys", async () => { + const config = getTestTurboConfig(); + const { hasKeys, envVars } = hasLegacyEnvVarDependencies(config); + expect(hasKeys).toEqual(true); + expect(envVars).toMatchInlineSnapshot(` + Array [ + "$GLOBAL_ENV_KEY", + "$TASK_ENV_KEY", + "$ANOTHER_ENV_KEY", + ] + `); + }); + + it("finds env keys in legacy turbo.json - multiple pipeline keys", async () => { + const config = getTestTurboConfig({ + pipeline: { test: { dependsOn: ["$MY_ENV"] } }, + }); + const { hasKeys, envVars } = hasLegacyEnvVarDependencies(config); + expect(hasKeys).toEqual(true); + expect(envVars).toMatchInlineSnapshot(` + Array [ + "$GLOBAL_ENV_KEY", + "$MY_ENV", + "$TASK_ENV_KEY", + "$ANOTHER_ENV_KEY", + ] + `); + }); + + it("finds env keys in legacy turbo.json - no keys", async () => { + // override to exclude keys + const config = getTestTurboConfig({ + globalDependencies: [], + pipeline: { build: { dependsOn: [] } }, + }); + const { hasKeys, envVars } = hasLegacyEnvVarDependencies(config); + expect(hasKeys).toEqual(false); + expect(envVars).toMatchInlineSnapshot(`Array []`); + }); + + it("finds env keys in turbo.json - no global", async () => { + const { hasKeys, envVars } = hasLegacyEnvVarDependencies({ + pipeline: { build: { dependsOn: ["$cool"] } }, + }); + expect(hasKeys).toEqual(true); + expect(envVars).toMatchInlineSnapshot(` + Array [ + "$cool", + ] + `); + }); + }); + + describe("migratePipeline - utility", () => { + it("migrates pipeline with env var dependencies", async () => { + const config = getTestTurboConfig(); + const { build } = config.pipeline; + const pipeline = migratePipeline(build); + expect(pipeline).toHaveProperty("env"); + expect(pipeline?.env).toMatchInlineSnapshot(` + Array [ + "TASK_ENV_KEY", + "ANOTHER_ENV_KEY", + ] + `); + expect(pipeline?.dependsOn).toMatchInlineSnapshot(` + Array [ + "^build", + ] + `); + }); + + it("migrates pipeline with no env var dependencies", async () => { + const config = getTestTurboConfig(); + const { test } = config.pipeline; + const pipeline = migratePipeline(test); + expect(pipeline.env).toBeUndefined(); + expect(pipeline?.dependsOn).toMatchInlineSnapshot(` + Array [ + "^build", + ] + `); + }); + + it("migrates pipeline with existing env key", async () => { + const config = getTestTurboConfig({ + pipeline: { test: { env: ["$MY_ENV"], dependsOn: ["^build"] } }, + }); + const { test } = config.pipeline; + const pipeline = migratePipeline(test); + expect(pipeline).toHaveProperty("env"); + expect(pipeline?.env).toMatchInlineSnapshot(` + Array [ + "$MY_ENV", + ] + `); + expect(pipeline?.dependsOn).toMatchInlineSnapshot(` + Array [ + "^build", + ] + `); + }); + + it("migrates pipeline with incomplete env key", async () => { + const config = getTestTurboConfig({ + pipeline: { + test: { env: ["$MY_ENV"], dependsOn: ["^build", "$SUPER_COOL"] }, + }, + }); + const { test } = config.pipeline; + const pipeline = migratePipeline(test); + expect(pipeline).toHaveProperty("env"); + expect(pipeline?.env).toMatchInlineSnapshot(` + Array [ + "$MY_ENV", + "SUPER_COOL", + ] + `); + expect(pipeline?.dependsOn).toMatchInlineSnapshot(` + Array [ + "^build", + ] + `); + }); + + it("migrates pipeline with duplicate env keys", async () => { + const config = getTestTurboConfig({ + pipeline: { + test: { env: ["$MY_ENV"], dependsOn: ["^build", "$MY_ENV"] }, + }, + }); + const { test } = config.pipeline; + const pipeline = migratePipeline(test); + expect(pipeline).toHaveProperty("env"); + expect(pipeline?.env).toMatchInlineSnapshot(` + Array [ + "$MY_ENV", + "MY_ENV", + ] + `); + expect(pipeline?.dependsOn).toMatchInlineSnapshot(` + Array [ + "^build", + ] + `); + }); + }); + + describe("migrateConfig - utility", () => { + it("migrates config with env var dependencies", async () => { + const config = getTestTurboConfig(); + const pipeline = migrateConfig(config); + expect(pipeline).toMatchInlineSnapshot(` + Object { + "$schema": "./docs/public/schema.json", + "globalEnv": Array [ + "GLOBAL_ENV_KEY", + ], + "pipeline": Object { + "build": Object { + "dependsOn": Array [ + "^build", + ], + "env": Array [ + "TASK_ENV_KEY", + "ANOTHER_ENV_KEY", + ], + "outputs": Array [ + "dist/**/*", + ".next/**/*", + "!.next/cache/**", + ], + }, + "dev": Object { + "cache": false, + }, + "lint": Object { + "outputs": Array [], + }, + "test": Object { + "dependsOn": Array [ + "^build", + ], + "outputs": Array [ + "coverage/**/*", + ], + }, + }, + } + `); + }); + + it("migrates config with no env var dependencies", async () => { + const config = getTestTurboConfig({ + globalDependencies: [], + pipeline: { + build: { dependsOn: ["^build"] }, + }, + }); + const pipeline = migrateConfig(config); + expect(pipeline).toMatchInlineSnapshot(` + Object { + "$schema": "./docs/public/schema.json", + "pipeline": Object { + "build": Object { + "dependsOn": Array [ + "^build", + ], + "outputs": Array [ + "dist/**/*", + ".next/**/*", + "!.next/cache/**", + ], + }, + "dev": Object { + "cache": false, + }, + "lint": Object { + "outputs": Array [], + }, + "test": Object { + "dependsOn": Array [ + "^build", + ], + "outputs": Array [ + "coverage/**/*", + ], + }, + }, + } + `); + }); + + it("migrates config with inconsistent config", async () => { + const config = getTestTurboConfig({ + pipeline: { + test: { env: ["$MY_ENV"], dependsOn: ["^build", "$SUPER_COOL"] }, + }, + }); + const pipeline = migrateConfig(config); + expect(pipeline).toMatchInlineSnapshot(` + Object { + "$schema": "./docs/public/schema.json", + "globalEnv": Array [ + "GLOBAL_ENV_KEY", + ], + "pipeline": Object { + "build": Object { + "dependsOn": Array [ + "^build", + ], + "env": Array [ + "TASK_ENV_KEY", + "ANOTHER_ENV_KEY", + ], + "outputs": Array [ + "dist/**/*", + ".next/**/*", + "!.next/cache/**", + ], + }, + "dev": Object { + "cache": false, + }, + "lint": Object { + "outputs": Array [], + }, + "test": Object { + "dependsOn": Array [ + "^build", + ], + "env": Array [ + "$MY_ENV", + "SUPER_COOL", + ], + "outputs": Array [ + "coverage/**/*", + ], + }, + }, + } + `); + }); + + it("migrates config with duplicate env keys", async () => { + const config = getTestTurboConfig({ + pipeline: { + test: { env: ["$MY_ENV"], dependsOn: ["^build", "$MY_ENV"] }, + }, + }); + const pipeline = migrateConfig(config); + expect(pipeline).toMatchInlineSnapshot(` + Object { + "$schema": "./docs/public/schema.json", + "globalEnv": Array [ + "GLOBAL_ENV_KEY", + ], + "pipeline": Object { + "build": Object { + "dependsOn": Array [ + "^build", + ], + "env": Array [ + "TASK_ENV_KEY", + "ANOTHER_ENV_KEY", + ], + "outputs": Array [ + "dist/**/*", + ".next/**/*", + "!.next/cache/**", + ], + }, + "dev": Object { + "cache": false, + }, + "lint": Object { + "outputs": Array [], + }, + "test": Object { + "dependsOn": Array [ + "^build", + ], + "env": Array [ + "$MY_ENV", + "MY_ENV", + ], + "outputs": Array [ + "coverage/**/*", + ], + }, + }, + } + `); + }); + }); + + describe("transform", () => { + const { useFixture } = setupTestFixtures({ + directory: __dirname, + test: "migrate-env-var-dependencies", + }); + + it("migrates turbo.json env var dependencies - basic", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "env-dependencies", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + globalDependencies: [".env"], + globalEnv: ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"], + pipeline: { + build: { + dependsOn: ["^build"], + env: ["PROD_API_KEY"], + outputs: [".next/**", "!.next/cache/**"], + }, + dev: { + cache: false, + }, + lint: { + dependsOn: [], + env: ["IS_CI"], + outputs: [], + }, + test: { + dependsOn: ["test"], + env: ["IS_CI"], + outputs: [], + }, + }, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "modified", + "additions": 4, + "deletions": 4, + }, + } + `); + }); + + it("migrates turbo.json env var dependencies - workspace configs", async () => { + // load the fixture for the test + const { root, readJson } = useFixture({ + fixture: "workspace-configs", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(readJson("turbo.json") || "{}").toStrictEqual({ + $schema: "https://turbo.build/schema.json", + globalDependencies: [".env"], + globalEnv: ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"], + pipeline: { + build: { + dependsOn: ["^build"], + env: ["PROD_API_KEY"], + outputs: [".next/**", "!.next/cache/**"], + }, + dev: { + cache: false, + }, + lint: { + dependsOn: [], + env: ["IS_TEST"], + outputs: [], + }, + test: { + dependsOn: ["test"], + env: ["IS_CI"], + outputs: [], + }, + }, + }); + + expect(readJson("apps/web/turbo.json") || "{}").toStrictEqual({ + $schema: "https://turbo.build/schema.json", + extends: ["//"], + pipeline: { + build: { + // old + dependsOn: ["build"], + // new + env: ["ENV_1", "ENV_2"], + }, + }, + }); + + expect(readJson("packages/ui/turbo.json") || "{}").toStrictEqual({ + $schema: "https://turbo.build/schema.json", + extends: ["//"], + pipeline: { + build: { + dependsOn: [], + env: ["IS_SERVER"], + }, + }, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "apps/web/turbo.json": Object { + "action": "modified", + "additions": 1, + "deletions": 0, + }, + "packages/ui/turbo.json": Object { + "action": "modified", + "additions": 1, + "deletions": 1, + }, + "turbo.json": Object { + "action": "modified", + "additions": 4, + "deletions": 4, + }, + } + `); + }); + + it("migrates turbo.json env var dependencies - repeat run", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "env-dependencies", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + globalDependencies: [".env"], + globalEnv: ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"], + pipeline: { + build: { + dependsOn: ["^build"], + env: ["PROD_API_KEY"], + outputs: [".next/**", "!.next/cache/**"], + }, + dev: { + cache: false, + }, + lint: { + dependsOn: [], + env: ["IS_CI"], + outputs: [], + }, + test: { + dependsOn: ["test"], + env: ["IS_CI"], + outputs: [], + }, + }, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "modified", + "additions": 4, + "deletions": 4, + }, + } + `); + + // run the transformer + const repeatResult = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(repeatResult.fatalError).toBeUndefined(); + expect(repeatResult.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + } + `); + }); + + it("migrates turbo.json env var dependencies - dry", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "env-dependencies", + }); + + const turboJson = JSON.parse(read("turbo.json") || "{}"); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: true, print: false }, + }); + + // make sure it didn't change + expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "skipped", + "additions": 4, + "deletions": 4, + }, + } + `); + }); + + it("migrates turbo.json env var dependencies - print", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "env-dependencies", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: true }, + }); + + expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + globalEnv: ["NEXT_PUBLIC_API_KEY", "STRIPE_API_KEY"], + globalDependencies: [".env"], + pipeline: { + build: { + dependsOn: ["^build"], + env: ["PROD_API_KEY"], + outputs: [".next/**", "!.next/cache/**"], + }, + dev: { + cache: false, + }, + lint: { + dependsOn: [], + env: ["IS_CI"], + outputs: [], + }, + test: { + dependsOn: ["test"], + env: ["IS_CI"], + outputs: [], + }, + }, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "modified", + "additions": 4, + "deletions": 4, + }, + } + `); + }); + + it("migrates turbo.json env var dependencies - dry & print", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "env-dependencies", + }); + + const turboJson = JSON.parse(read("turbo.json") || "{}"); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: true, print: true }, + }); + + // make sure it didn't change + expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "skipped", + "additions": 4, + "deletions": 4, + }, + } + `); + }); + + it("does not change turbo.json if already migrated", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "migrated-env-dependencies", + }); + + const turboJson = JSON.parse(read("turbo.json") || "{}"); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + } + `); + }); + + it("errors if no turbo.json can be found", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "no-turbo-json", + }); + + expect(read("turbo.json")).toBeUndefined(); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(read("turbo.json")).toBeUndefined(); + expect(result.fatalError).toBeDefined(); + expect(result.fatalError?.message).toMatch( + /No turbo\.json found at .*?\. Is the path correct\?/ + ); + }); + + it("errors if package.json config exists and has not been migrated", async () => { + // load the fixture for the test + const { root } = useFixture({ + fixture: "old-config", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(result.fatalError).toBeDefined(); + expect(result.fatalError?.message).toMatch( + 'turbo" key detected in package.json. Run `npx @turbo/codemod transform create-turbo-config` first' + ); + }); + }); +}); diff --git a/packages/turbo-codemod/__tests__/migrate.test.ts b/packages/turbo-codemod/__tests__/migrate.test.ts new file mode 100644 index 0000000..652ea41 --- /dev/null +++ b/packages/turbo-codemod/__tests__/migrate.test.ts @@ -0,0 +1,761 @@ +import { MigrateCommandArgument } from "../src/commands"; +import migrate from "../src/commands/migrate"; +import { setupTestFixtures, spyExit } from "@turbo/test-utils"; +import childProcess from "child_process"; +import * as checkGitStatus from "../src/utils/checkGitStatus"; +import * as getCurrentVersion from "../src/commands/migrate/steps/getCurrentVersion"; +import * as getLatestVersion from "../src/commands/migrate/steps/getLatestVersion"; +import * as getTurboUpgradeCommand from "../src/commands/migrate/steps/getTurboUpgradeCommand"; +import * as workspaceImplementation from "../src/utils/getPackageManager"; +import * as getPackageManagerVersion from "../src/utils/getPackageManagerVersion"; + +describe("migrate", () => { + const mockExit = spyExit(); + const { useFixture } = setupTestFixtures({ + directory: __dirname, + test: "migrate", + }); + + it("migrates from 1.0.0 to 1.7.0", async () => { + const { root, readJson } = useFixture({ + fixture: "old-turbo", + }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.0.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue("1.7.0"); + const mockedGetTurboUpgradeCommand = jest + .spyOn(getTurboUpgradeCommand, "default") + .mockReturnValue("pnpm install -g turbo@latest"); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetWorkspaceImplementation = jest + .spyOn(workspaceImplementation, "default") + .mockReturnValue(packageManager); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: false, + }); + + expect(readJson("package.json")).toStrictEqual({ + dependencies: {}, + devDependencies: { + turbo: "1.0.0", + }, + name: "no-turbo-json", + packageManager: "pnpm@1.2.3", + version: "1.0.0", + }); + expect(readJson("turbo.json")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + build: { + outputs: [".next/**", "!.next/cache/**"], + }, + dev: { + cache: false, + }, + lint: {}, + test: { + outputs: ["dist/**", "build/**"], + }, + }, + }); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); + expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + mockedGetTurboUpgradeCommand.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + mockedGetWorkspaceImplementation.mockRestore(); + }); + + it("migrates from 1.0.0 to 1.2.0 (dry run)", async () => { + const { root, readJson } = useFixture({ + fixture: "old-turbo", + }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.0.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue("1.2.0"); + const mockedGetTurboUpgradeCommand = jest + .spyOn(getTurboUpgradeCommand, "default") + .mockReturnValue("pnpm install -g turbo@latest"); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetWorkspaceImplementation = jest + .spyOn(workspaceImplementation, "default") + .mockReturnValue(packageManager); + + const packageJson = readJson("package.json"); + const turboJson = readJson("turbo.json"); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: true, + print: false, + install: true, + }); + + // make sure nothing changed + expect(readJson("package.json")).toStrictEqual(packageJson); + expect(readJson("turbo.json")).toStrictEqual(turboJson); + + // verify mocks were called + expect(mockedCheckGitStatus).not.toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); + expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + mockedGetTurboUpgradeCommand.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + mockedGetWorkspaceImplementation.mockRestore(); + }); + + it("next version can be passed as an option", async () => { + const { root, readJson } = useFixture({ + fixture: "old-turbo", + }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.0.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue("1.7.0"); + const mockedGetTurboUpgradeCommand = jest + .spyOn(getTurboUpgradeCommand, "default") + .mockReturnValue("pnpm install -g turbo@latest"); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetWorkspaceImplementation = jest + .spyOn(workspaceImplementation, "default") + .mockReturnValue(packageManager); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: false, + to: "1.7.0", + }); + + expect(readJson("package.json")).toStrictEqual({ + dependencies: {}, + devDependencies: { + turbo: "1.0.0", + }, + name: "no-turbo-json", + packageManager: "pnpm@1.2.3", + version: "1.0.0", + }); + expect(readJson("turbo.json")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + build: { + outputs: [".next/**", "!.next/cache/**"], + }, + dev: { + cache: false, + }, + test: { + outputs: ["dist/**", "build/**"], + }, + lint: {}, + }, + }); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); + expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + mockedGetTurboUpgradeCommand.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + mockedGetWorkspaceImplementation.mockRestore(); + }); + + it("current version can be passed as an option", async () => { + const { root, readJson } = useFixture({ + fixture: "old-turbo", + }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue("1.7.0"); + const mockedGetTurboUpgradeCommand = jest + .spyOn(getTurboUpgradeCommand, "default") + .mockReturnValue("pnpm install -g turbo@latest"); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + + const mockedGetWorkspaceImplementation = jest + .spyOn(workspaceImplementation, "default") + .mockReturnValue(packageManager); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: false, + from: "1.0.0", + }); + + expect(readJson("package.json")).toStrictEqual({ + dependencies: {}, + devDependencies: { + turbo: "1.0.0", + }, + name: "no-turbo-json", + packageManager: "pnpm@1.2.3", + version: "1.0.0", + }); + expect(readJson("turbo.json")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + build: { + outputs: [".next/**", "!.next/cache/**"], + }, + dev: { + cache: false, + }, + lint: {}, + test: { + outputs: ["dist/**", "build/**"], + }, + }, + }); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); + expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetLatestVersion.mockRestore(); + mockedGetTurboUpgradeCommand.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + mockedGetWorkspaceImplementation.mockRestore(); + }); + + it("exits if the current version is the same as the new version", async () => { + const { root } = useFixture({ + fixture: "old-turbo", + }); + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.7.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue("1.7.0"); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: false, + }); + + expect(mockExit.exit).toHaveBeenCalledWith(0); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + }); + + it("continues when migration doesn't require codemods", async () => { + const { root } = useFixture({ + fixture: "old-turbo", + }); + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.3.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue("1.3.1"); + const mockedGetTurboUpgradeCommand = jest + .spyOn(getTurboUpgradeCommand, "default") + .mockReturnValue("npm install turbo@1.3.1"); + const mockExecSync = jest + .spyOn(childProcess, "execSync") + .mockReturnValue("installed"); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: true, + }); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); + expect(mockExecSync).toHaveBeenCalledWith("npm install turbo@1.3.1", { + cwd: root, + }); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + mockedGetTurboUpgradeCommand.mockRestore(); + mockExecSync.mockRestore(); + }); + + it("installs the correct turbo version", async () => { + const { root, readJson } = useFixture({ + fixture: "old-turbo", + }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.0.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue("1.7.0"); + const mockedGetTurboUpgradeCommand = jest + .spyOn(getTurboUpgradeCommand, "default") + .mockReturnValue("pnpm install -g turbo@1.7.0"); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetWorkspaceImplementation = jest + .spyOn(workspaceImplementation, "default") + .mockReturnValue(packageManager); + const mockExecSync = jest + .spyOn(childProcess, "execSync") + .mockReturnValue("installed"); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: true, + }); + + expect(readJson("package.json")).toStrictEqual({ + dependencies: {}, + devDependencies: { + turbo: "1.0.0", + }, + name: "no-turbo-json", + packageManager: "pnpm@1.2.3", + version: "1.0.0", + }); + expect(readJson("turbo.json")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + build: { + outputs: [".next/**", "!.next/cache/**"], + }, + dev: { + cache: false, + }, + lint: {}, + test: { + outputs: ["dist/**", "build/**"], + }, + }, + }); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); + expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + expect(mockExecSync).toHaveBeenCalled(); + expect(mockExecSync).toHaveBeenCalledWith("pnpm install -g turbo@1.7.0", { + cwd: root, + }); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + mockedGetTurboUpgradeCommand.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + mockedGetWorkspaceImplementation.mockRestore(); + mockExecSync.mockRestore(); + }); + + it("fails gracefully when the correct upgrade command cannot be found", async () => { + const { root, readJson } = useFixture({ + fixture: "old-turbo", + }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.0.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue("1.7.0"); + const mockedGetTurboUpgradeCommand = jest + .spyOn(getTurboUpgradeCommand, "default") + .mockReturnValue(undefined); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetWorkspaceImplementation = jest + .spyOn(workspaceImplementation, "default") + .mockReturnValue(packageManager); + const mockExecSync = jest + .spyOn(childProcess, "execSync") + .mockReturnValue("installed"); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: true, + }); + + expect(readJson("package.json")).toStrictEqual({ + dependencies: {}, + devDependencies: { + turbo: "1.0.0", + }, + name: "no-turbo-json", + packageManager: "pnpm@1.2.3", + version: "1.0.0", + }); + expect(readJson("turbo.json")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + build: { + outputs: [".next/**", "!.next/cache/**"], + }, + dev: { + cache: false, + }, + lint: {}, + test: { + outputs: ["dist/**", "build/**"], + }, + }, + }); + + expect(mockExit.exit).toHaveBeenCalledWith(1); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + expect(mockedGetTurboUpgradeCommand).toHaveBeenCalled(); + expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + expect(mockExecSync).not.toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + mockedGetTurboUpgradeCommand.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + mockedGetWorkspaceImplementation.mockRestore(); + mockExecSync.mockRestore(); + }); + + it("exits if current version is not passed and cannot be inferred", async () => { + const { root } = useFixture({ + fixture: "old-turbo", + }); + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue(undefined); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: false, + }); + + expect(mockExit.exit).toHaveBeenCalledWith(1); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + }); + + it("exits if latest version is not passed and cannot be inferred", async () => { + const { root } = useFixture({ + fixture: "old-turbo", + }); + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.5.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue(undefined); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: false, + }); + + expect(mockExit.exit).toHaveBeenCalledWith(1); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + }); + + it("exits if latest version throws", async () => { + const { root } = useFixture({ + fixture: "old-turbo", + }); + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.5.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockRejectedValue(new Error("failed to fetch version")); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: false, + }); + + expect(mockExit.exit).toHaveBeenCalledWith(1); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + }); + + it("exits if any transforms encounter an error", async () => { + const { root } = useFixture({ + fixture: "old-turbo", + }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetCurrentVersion = jest + .spyOn(getCurrentVersion, "default") + .mockReturnValue("1.0.0"); + const mockedGetLatestVersion = jest + .spyOn(getLatestVersion, "default") + .mockResolvedValue("1.7.0"); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetWorkspaceImplementation = jest + .spyOn(workspaceImplementation, "default") + .mockReturnValue(packageManager); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: true, + print: false, + install: true, + }); + + expect(mockExit.exit).toHaveBeenCalledWith(1); + + // verify mocks were called + expect(mockedCheckGitStatus).not.toHaveBeenCalled(); + expect(mockedGetCurrentVersion).toHaveBeenCalled(); + expect(mockedGetLatestVersion).toHaveBeenCalled(); + expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockedGetWorkspaceImplementation).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetCurrentVersion.mockRestore(); + mockedGetLatestVersion.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + mockedGetWorkspaceImplementation.mockRestore(); + }); + + it("exits if invalid directory is passed", async () => { + const { root } = useFixture({ + fixture: "old-turbo", + }); + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + + await migrate("~/path/that/does/not/exist" as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: false, + }); + + expect(mockExit.exit).toHaveBeenCalledWith(1); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + }); + + it("exits if directory with no repo is passed", async () => { + const { root } = useFixture({ + fixture: "no-repo", + }); + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + + await migrate(root as MigrateCommandArgument, { + force: false, + dry: false, + print: false, + install: false, + }); + + expect(mockExit.exit).toHaveBeenCalledWith(1); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + }); +}); diff --git a/packages/turbo-codemod/__tests__/set-default-outputs.test.ts b/packages/turbo-codemod/__tests__/set-default-outputs.test.ts new file mode 100644 index 0000000..4a71fa7 --- /dev/null +++ b/packages/turbo-codemod/__tests__/set-default-outputs.test.ts @@ -0,0 +1,391 @@ +import { transformer } from "../src/transforms/set-default-outputs"; +import { setupTestFixtures } from "@turbo/test-utils"; + +describe("set-default-outputs", () => { + const { useFixture } = setupTestFixtures({ + directory: __dirname, + test: "set-default-outputs", + }); + it("migrates turbo.json outputs - basic", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "old-outputs", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + "build-one": { + outputs: ["foo"], + }, + "build-two": {}, + "build-three": { + outputs: ["dist/**", "build/**"], + }, + }, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "modified", + "additions": 2, + "deletions": 1, + }, + } + `); + }); + + it("migrates turbo.json outputs - workspace configs", async () => { + // load the fixture for the test + const { root, readJson } = useFixture({ + fixture: "workspace-configs", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(readJson("turbo.json") || "{}").toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + "build-one": { + outputs: ["foo"], + }, + "build-two": {}, + "build-three": { + outputs: ["dist/**", "build/**"], + }, + }, + }); + + expect(readJson("apps/docs/turbo.json") || "{}").toStrictEqual({ + $schema: "https://turbo.build/schema.json", + extends: ["//"], + pipeline: { + build: { + outputs: ["dist/**", "build/**"], + }, + }, + }); + + expect(readJson("apps/web/turbo.json") || "{}").toStrictEqual({ + $schema: "https://turbo.build/schema.json", + extends: ["//"], + pipeline: { + build: {}, + }, + }); + + expect(readJson("packages/ui/turbo.json") || "{}").toStrictEqual({ + $schema: "https://turbo.build/schema.json", + extends: ["//"], + pipeline: { + "build-three": { + outputs: ["dist/**", "build/**"], + }, + }, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "apps/docs/turbo.json": Object { + "action": "modified", + "additions": 1, + "deletions": 1, + }, + "apps/web/turbo.json": Object { + "action": "modified", + "additions": 1, + "deletions": 0, + }, + "packages/ui/turbo.json": Object { + "action": "modified", + "additions": 1, + "deletions": 1, + }, + "turbo.json": Object { + "action": "modified", + "additions": 2, + "deletions": 1, + }, + } + `); + }); + + it("migrates turbo.json outputs - dry", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "old-outputs", + }); + + const turboJson = JSON.parse(read("turbo.json") || "{}"); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: true, print: false }, + }); + + // make sure it didn't change + expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "skipped", + "additions": 2, + "deletions": 1, + }, + } + `); + }); + + it("migrates turbo.json outputs - print", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "old-outputs", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: true }, + }); + + expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + "build-one": { + outputs: ["foo"], + }, + "build-two": {}, + "build-three": { + outputs: ["dist/**", "build/**"], + }, + }, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "modified", + "additions": 2, + "deletions": 1, + }, + } + `); + }); + + it("migrates turbo.json outputs - dry & print", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "old-outputs", + }); + + const turboJson = JSON.parse(read("turbo.json") || "{}"); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: true, print: false }, + }); + + // make sure it didn't change + expect(JSON.parse(read("turbo.json") || "{}")).toEqual(turboJson); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "skipped", + "additions": 2, + "deletions": 1, + }, + } + `); + }); + + it("migrates turbo.json outputs - invalid", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "invalid-outputs", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + "build-one": { + outputs: ["foo"], + }, + "build-two": {}, + "build-three": { + outputs: ["dist/**", "build/**"], + }, + "garbage-in-numeric-0": { + outputs: ["dist/**", "build/**"], + }, + "garbage-in-numeric": { + outputs: 42, + }, + "garbage-in-string": { + outputs: "string", + }, + "garbage-in-empty-string": { + outputs: ["dist/**", "build/**"], + }, + "garbage-in-null": { + outputs: ["dist/**", "build/**"], + }, + "garbage-in-false": { + outputs: ["dist/**", "build/**"], + }, + "garbage-in-true": { + outputs: true, + }, + "garbage-in-object": { + outputs: {}, + }, + }, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "modified", + "additions": 6, + "deletions": 5, + }, + } + `); + }); + + it("migrates turbo.json outputs - config with no pipeline", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "no-pipeline", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + globalDependencies: ["$NEXT_PUBLIC_API_KEY", "$STRIPE_API_KEY", ".env"], + pipeline: {}, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "unchanged", + "additions": 0, + "deletions": 0, + }, + } + `); + }); + + it("migrates turbo.json outputs - config with no outputs", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "no-outputs", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(JSON.parse(read("turbo.json") || "{}")).toStrictEqual({ + $schema: "https://turbo.build/schema.json", + pipeline: { + "build-one": { + dependsOn: ["build-two"], + outputs: ["dist/**", "build/**"], + }, + "build-two": { + cache: false, + }, + "build-three": { + persistent: true, + outputs: ["dist/**", "build/**"], + }, + }, + }); + + expect(result.fatalError).toBeUndefined(); + expect(result.changes).toMatchInlineSnapshot(` + Object { + "turbo.json": Object { + "action": "modified", + "additions": 2, + "deletions": 0, + }, + } + `); + }); + + it("errors if no turbo.json can be found", async () => { + // load the fixture for the test + const { root, read } = useFixture({ + fixture: "no-turbo-json", + }); + + expect(read("turbo.json")).toBeUndefined(); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(read("turbo.json")).toBeUndefined(); + expect(result.fatalError).toBeDefined(); + expect(result.fatalError?.message).toMatch( + /No turbo\.json found at .*?\. Is the path correct\?/ + ); + }); + + it("errors if package.json config exists and has not been migrated", async () => { + // load the fixture for the test + const { root } = useFixture({ + fixture: "old-config", + }); + + // run the transformer + const result = transformer({ + root, + options: { force: false, dry: false, print: false }, + }); + + expect(result.fatalError).toBeDefined(); + expect(result.fatalError?.message).toMatch( + 'turbo" key detected in package.json. Run `npx @turbo/codemod transform create-turbo-config` first' + ); + }); +}); diff --git a/packages/turbo-codemod/__tests__/transform.test.ts b/packages/turbo-codemod/__tests__/transform.test.ts new file mode 100644 index 0000000..abd015d --- /dev/null +++ b/packages/turbo-codemod/__tests__/transform.test.ts @@ -0,0 +1,172 @@ +import transform from "../src/commands/transform"; +import { MigrateCommandArgument } from "../src/commands"; +import { setupTestFixtures, spyExit } from "@turbo/test-utils"; +import * as checkGitStatus from "../src/utils/checkGitStatus"; +import * as getPackageManager from "../src/utils/getPackageManager"; +import * as getPackageManagerVersion from "../src/utils/getPackageManagerVersion"; + +describe("transform", () => { + const mockExit = spyExit(); + const { useFixture } = setupTestFixtures({ + directory: __dirname, + test: "transform", + }); + + it("runs the selected transform", async () => { + const { root, readJson } = useFixture({ + fixture: "basic", + }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + await transform( + "add-package-manager" as MigrateCommandArgument, + root as MigrateCommandArgument, + { + list: false, + force: false, + dry: false, + print: false, + } + ); + + expect(readJson("package.json")).toStrictEqual({ + dependencies: {}, + devDependencies: { + turbo: "1.0.0", + }, + name: "transform-basic", + packageManager: "pnpm@1.2.3", + version: "1.0.0", + }); + + // verify mocks were called + expect(mockedCheckGitStatus).toHaveBeenCalled(); + expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockedGetPackageManager).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + mockedGetPackageManager.mockRestore(); + }); + + it("runs the selected transform - dry & print", async () => { + const { root, readJson } = useFixture({ + fixture: "basic", + }); + + const packageManager = "pnpm"; + const packageManagerVersion = "1.2.3"; + + // setup mocks + const mockedCheckGitStatus = jest + .spyOn(checkGitStatus, "default") + .mockReturnValue(undefined); + const mockedGetPackageManagerVersion = jest + .spyOn(getPackageManagerVersion, "default") + .mockReturnValue(packageManagerVersion); + const mockedGetPackageManager = jest + .spyOn(getPackageManager, "default") + .mockReturnValue(packageManager); + + await transform( + "add-package-manager" as MigrateCommandArgument, + root as MigrateCommandArgument, + { + list: false, + force: false, + dry: true, + print: true, + } + ); + + expect(readJson("package.json")).toStrictEqual({ + dependencies: {}, + devDependencies: { + turbo: "1.0.0", + }, + name: "transform-basic", + version: "1.0.0", + }); + + // verify mocks were called + expect(mockedCheckGitStatus).not.toHaveBeenCalled(); + expect(mockedGetPackageManagerVersion).toHaveBeenCalled(); + expect(mockedGetPackageManager).toHaveBeenCalled(); + + // restore mocks + mockedCheckGitStatus.mockRestore(); + mockedGetPackageManagerVersion.mockRestore(); + mockedGetPackageManager.mockRestore(); + }); + + it("lists transforms", async () => { + const { root } = useFixture({ + fixture: "basic", + }); + + await transform( + "add-package-manager" as MigrateCommandArgument, + root as MigrateCommandArgument, + { + list: true, + force: false, + dry: false, + print: false, + } + ); + + expect(mockExit.exit).toHaveBeenCalledWith(0); + }); + + it("exits on invalid transform", async () => { + const { root } = useFixture({ + fixture: "basic", + }); + + await transform( + "not-a-real-option" as MigrateCommandArgument, + root as MigrateCommandArgument, + { + list: false, + force: false, + dry: false, + print: false, + } + ); + + expect(mockExit.exit).toHaveBeenCalledWith(1); + }); + + it("exits on invalid directory", async () => { + const { root } = useFixture({ + fixture: "basic", + }); + + await transform( + "add-package-manager" as MigrateCommandArgument, + "~/path/that/does/not/exist" as MigrateCommandArgument, + { + list: false, + force: false, + dry: false, + print: false, + } + ); + + expect(mockExit.exit).toHaveBeenCalledWith(1); + }); +}); |
