diff --git a/src/test/groovy/com/github/gradle/node/bun/task/BunTask_integTest.groovy b/src/test/groovy/com/github/gradle/node/bun/task/BunTask_integTest.groovy new file mode 100644 index 00000000..64d29f3b --- /dev/null +++ b/src/test/groovy/com/github/gradle/node/bun/task/BunTask_integTest.groovy @@ -0,0 +1,172 @@ +package com.github.gradle.node.bun.task + +import com.github.gradle.AbstractIntegTest +import com.github.gradle.node.Versions +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Rule +import org.junit.contrib.java.lang.system.EnvironmentVariables +import spock.lang.Ignore +import spock.lang.IgnoreIf + +@IgnoreIf({ os.windows }) +class BunTask_integTest extends AbstractIntegTest { + def 'execute bun command with a package.json file and check inputs up-to-date detection (#gv.version)'() { + given: + gradleVersion = gv + copyResources('fixtures/bun/', '') + copyResources('fixtures/javascript-project/', '') + + when: + def result1 = build(":test") + + then: + result1.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result1.task(":bunSetup").outcome == TaskOutcome.SUCCESS + result1.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result1.task(":test").outcome == TaskOutcome.SUCCESS + result1.output.contains("1 passing") + + when: + def result2 = build(":test") + + then: + result2.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result2.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result2.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result2.task(":test").outcome == TaskOutcome.UP_TO_DATE + + when: + def result3 = build(":test", "-DchangeInputs=true") + + then: + result3.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result3.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result3.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result3.task(":test").outcome == TaskOutcome.SUCCESS + + when: + def result4 = build(":version") + + then: + result4.task(":version").outcome == TaskOutcome.SUCCESS + result4.output.contains("> Task :version${System.lineSeparator()}1.0.3") + + where: + gv << GRADLE_VERSIONS_UNDER_TEST + } + + def 'execute bun command with custom execution configuration and check up-to-date-detection (#gv.version)'() { + given: + gradleVersion = gv + copyResources('fixtures/bun-env/', '') + + when: + def result1 = build(":env") + + then: + result1.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result1.task(":bunSetup").outcome == TaskOutcome.SUCCESS + result1.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result1.task(":env").outcome == TaskOutcome.SUCCESS + environmentDumpContainsPathVariable(result1.output) + + when: + def result2 = build(":env", "-DcustomEnv=true") + + then: + result2.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result2.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result2.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result2.task(":env").outcome == TaskOutcome.SUCCESS + result2.output.contains("CUSTOM=custom value") + + when: + System.setProperty("NEW_ENV_VARIABLE", "Let's make the whole environment change") + def result3 = build(":env", "-DcustomEnv=true") + + then: + result3.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result3.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result3.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result3.task(":env").outcome == TaskOutcome.UP_TO_DATE + + when: + def result4 = build(":env", "-DignoreExitValue=true", "-DnotExistingCommand=true") + + then: + result4.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result4.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result4.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result4.task(":env").outcome == TaskOutcome.SUCCESS + result4.output.contains("script not found \"notExistingCommand\"") + + when: + def result5 = buildAndFail(":env", "-DnotExistingCommand=true") + + then: + result5.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result5.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result5.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result5.task(":env").outcome == TaskOutcome.FAILED + result5.output.contains("script not found \"notExistingCommand\"") + + when: + def result6 = build(":pwd") + + then: + result6.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result6.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result6.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result6.task(":pwd").outcome == TaskOutcome.SUCCESS + result6.output.contains("Working directory is '${projectDir}'") + + when: + def result7 = build(":pwd", "-DcustomWorkingDir=true") + + then: + result7.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result7.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result7.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result7.task(":pwd").outcome == TaskOutcome.UP_TO_DATE + + when: + def result8 = build(":pwd", "-DcustomWorkingDir=true", "--rerun-tasks") + + then: + result8.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result8.task(":bunSetup").outcome == TaskOutcome.SUCCESS + result8.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result8.task(":pwd").outcome == TaskOutcome.SUCCESS + def expectedWorkingDirectory = "${projectDir}${File.separator}build${File.separator}customWorkingDirectory" + result8.output.contains("Working directory is '${expectedWorkingDirectory}'") + new File(expectedWorkingDirectory).isDirectory() + + when: + def result9 = build(":version") + + then: + result9.task(":version").outcome == TaskOutcome.SUCCESS + result9.output.contains("> Task :version${System.lineSeparator()}1.0.3") + + where: + gv << GRADLE_VERSIONS_UNDER_TEST + } + + @Ignore("Should it even work that way?") + def 'execute bun command using the bun version specified in the package.json file (#gv.version)'() { + given: + gradleVersion = gv + copyResources('fixtures/bun/', '') + copyResources('fixtures/bun-present/', '') + + when: + def result = build(":version") + + then: + result.task(":version").outcome == TaskOutcome.SUCCESS + result.output.contains("> Task :version${System.lineSeparator()}1.0.0") + + where: + gv << GRADLE_VERSIONS_UNDER_TEST + } +} diff --git a/src/test/groovy/com/github/gradle/node/bun/task/BunxTask_integTest.groovy b/src/test/groovy/com/github/gradle/node/bun/task/BunxTask_integTest.groovy new file mode 100644 index 00000000..558d4bdf --- /dev/null +++ b/src/test/groovy/com/github/gradle/node/bun/task/BunxTask_integTest.groovy @@ -0,0 +1,216 @@ +package com.github.gradle.node.bun.task + +import com.github.gradle.AbstractIntegTest +import com.github.gradle.node.NodeExtension +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Rule +import org.junit.contrib.java.lang.system.EnvironmentVariables +import spock.lang.Ignore +import spock.lang.IgnoreIf + +import java.util.regex.Pattern + +import static com.github.gradle.node.NodeExtension.DEFAULT_NPM_VERSION + +class BunxTask_integTest extends AbstractIntegTest { + def 'execute bunx command with no package.json file (#gv.version)'() { + given: + gradleVersion = gv + + writeBuild(''' + plugins { + id 'com.github.node-gradle.node' + } + + task camelCase(type: BunxTask) { + command = 'chcase-cli' + args = ['--help'] + } + ''') + + when: + def result = build(":camelCase") + + then: + result.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result.task(":bunSetup").outcome == TaskOutcome.SUCCESS + result.task(":camelCase").outcome == TaskOutcome.SUCCESS + result.output.contains("--case, -C Which case to convert to") + + where: + gv << GRADLE_VERSIONS_UNDER_TEST + } + + def 'execute bunx command with a package.json file and check inputs up-to-date detection (#gv.version)'() { + given: + gradleVersion = gv + + copyResources("fixtures/bunx/") + copyResources("fixtures/javascript-project/") + + when: + def result1 = build(":test") + + then: + result1.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result1.task(":bunSetup").outcome == TaskOutcome.SUCCESS + result1.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result1.task(":lint").outcome == TaskOutcome.SUCCESS + result1.task(":test").outcome == TaskOutcome.SUCCESS + result1.output.contains("5 problems (0 errors, 5 warnings)") + result1.output.contains("1 passing") + + when: + def result2 = build(":test") + + then: + result2.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result2.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result2.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result2.task(":lint").outcome == TaskOutcome.UP_TO_DATE + result2.task(":test").outcome == TaskOutcome.UP_TO_DATE + + when: + def result3 = build(":test", "-DchangeInputs=true") + + then: + result3.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result3.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result3.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result3.task(":lint").outcome == TaskOutcome.SUCCESS + result3.task(":test").outcome == TaskOutcome.SUCCESS + + where: + gv << GRADLE_VERSIONS_UNDER_TEST + } + + def 'execute bun pwd command with custom execution configuration and check up-to-date-detection'() { + given: + gradleVersion = gv + + copyResources("fixtures/bunx-env/") + copyResources("fixtures/env/") + + when: + def result7 = build(":pwd") + + then: + result7.task(":pwd").outcome == TaskOutcome.SUCCESS + result7.output.contains("workingDirectory='${projectDir}'") + + when: + def result8 = build(":pwd", "-DcustomWorkingDir=true") + + then: + result8.task(":pwd").outcome == TaskOutcome.UP_TO_DATE + + when: + def result9 = build(":pwd", "-DcustomWorkingDir=true", "--rerun-tasks") + + then: + result9.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result9.task(":bunSetup").outcome == TaskOutcome.SUCCESS + result9.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result9.task(":pwd").outcome == TaskOutcome.SUCCESS + def expectedWorkingDirectory = "${projectDir}${File.separator}build${File.separator}customWorkingDirectory" + result9.output.contains("workingDirectory='${expectedWorkingDirectory}'") + new File(expectedWorkingDirectory).isDirectory() + + where: + gv << GRADLE_VERSIONS_UNDER_TEST + } + + def 'execute bunx env command with custom execution configuration and check up-to-date-detection'() { + given: + gradleVersion = gv + + copyResources("fixtures/bunx-env/") + copyResources("fixtures/bun-env/package.json", "package.json") + + when: + def result1 = build(":env") + + then: + result1.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result1.task(":bunSetup").outcome == TaskOutcome.SUCCESS + result1.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result1.task(":env").outcome == TaskOutcome.SUCCESS + // Sometimes the PATH variable is not defined in Windows Powershell, but the PATHEXT is + Pattern.compile("^PATH(?:EXT)?=.+\$", Pattern.MULTILINE).matcher(result1.output).find() + + when: + def result2 = build(":env", "-DcustomEnv=true") + + then: + result2.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result2.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result2.task(":bunInstall").outcome == TaskOutcome.SUCCESS + result2.task(":env").outcome == TaskOutcome.SUCCESS + result2.output.contains("CUSTOM=custom value") + + when: + System.setProperty("NEW_ENV_VARIABLE", "Let's make the whole environment change") + def result3 = build(":env", "-DcustomEnv=true") + + then: + result3.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result3.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result3.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result3.task(":env").outcome == TaskOutcome.UP_TO_DATE + + when: + def result4 = build(":env", "-DignoreExitValue=true", "-DnotExistingCommand=true") + + then: + result4.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result4.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result4.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result4.task(":env").outcome == TaskOutcome.SUCCESS + result4.output.contains("E404") + + when: + def result5 = buildAndFail(":env", "-DnotExistingCommand=true") + + then: + result5.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result5.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result5.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result5.task(":env").outcome == TaskOutcome.FAILED + result5.output.contains("E404") + + when: + def result6 = build(":env", "-DoutputFile=true", "--stacktrace") + + then: + result6.task(":nodeSetup").outcome == TaskOutcome.SKIPPED + result6.task(":bunSetup").outcome == TaskOutcome.UP_TO_DATE + result6.task(":bunInstall").outcome == TaskOutcome.UP_TO_DATE + result6.task(":env").outcome == TaskOutcome.SUCCESS + !environmentDumpContainsPathVariable(result6.output) + def outputFile = file("build/standard-output.txt") + outputFile.exists() + environmentDumpContainsPathVariable(outputFile.text) + + where: + gv << GRADLE_VERSIONS_UNDER_TEST + } + + @Ignore("Should it even work that way?") + def 'execute bunx command using the npm version specified in the package.json file (#gv.version)'() { + given: + gradleVersion = gv + + copyResources("fixtures/bunx/") + copyResources("fixtures/bun-present/") + + when: + def result = build(":version") + + then: + result.task(":version").outcome == TaskOutcome.SUCCESS + result.output.contains("> Task :version${System.lineSeparator()}1.0.0") + + where: + gv << GRADLE_VERSIONS_UNDER_TEST + } +} diff --git a/src/test/resources/fixtures/bun-env/build.gradle b/src/test/resources/fixtures/bun-env/build.gradle new file mode 100644 index 00000000..507a14a0 --- /dev/null +++ b/src/test/resources/fixtures/bun-env/build.gradle @@ -0,0 +1,65 @@ +plugins { + id "com.github.node-gradle.node" +} + +node { + workDir = file("build/node") + bunVersion = '1.0.3' +} + +task env(type: BunTask) { + dependsOn bunInstall + bunCommand = ["run", "print-env"] + outputs.upToDateWhen { + true + } +} + +// This should be documented in the package.json file but it cannot be done because JSON does not accept comments. +// bun run forces the current directory but we can read the INIT_CWD environment variable to get the original working +// directory. This is the reason why we read the INIT_CWD variable and not the process.cwd() one in the script +// definition. +task pwd(type: BunTask) { + dependsOn bunInstall + bunCommand = ["run", "printcwd"] + outputs.upToDateWhen { + true + } +} + +task version(type: BunTask) { + dependsOn bunInstall + bunCommand = ["--version"] +} + +if (isPropertyEnabled("customEnv")) { + def qualifier = "custom" + env.environment = [CUSTOM: "${qualifier} value"] +} + +if (isPropertyEnabled("ignoreExitValue")) { + env.ignoreExitValue = true +} + +if (isPropertyEnabled("notExistingCommand")) { + env.bunCommand = ["notExistingCommand"] +} + +if (isPropertyEnabled("customWorkingDir")) { + pwd.workingDir = file("${project.buildDir}/customWorkingDirectory/") +} + +if (isPropertyEnabled("outputFile")) { + env.execOverrides { + standardOutput = new FileOutputStream("${buildDir}/standard-output.txt") + } +} + +def isPropertyEnabled(String name) { + def provider = providers.systemProperty(name) + if (org.gradle.util.GradleVersion.current() >= org.gradle.util.GradleVersion.version("7.4")) { + return provider.isPresent() + } else { + return provider.forUseAtConfigurationTime().isPresent() + } +} diff --git a/src/test/resources/fixtures/bun-env/package.json b/src/test/resources/fixtures/bun-env/package.json new file mode 100644 index 00000000..a57a2ac6 --- /dev/null +++ b/src/test/resources/fixtures/bun-env/package.json @@ -0,0 +1,11 @@ +{ + "name": "env", + "dependencies": { + "@bahmutov/print-env": "2.0.2", + "utils-eval": "1.0.1" + }, + "scripts": { + "print-env": "print-env PATH CUSTOM", + "printcwd": "jseval \"'Working directory is \\'' + process.env.PWD + '\\''\"" + } +} diff --git a/src/test/resources/fixtures/bun-present/package.json b/src/test/resources/fixtures/bun-present/package.json new file mode 100644 index 00000000..efbb7a1a --- /dev/null +++ b/src/test/resources/fixtures/bun-present/package.json @@ -0,0 +1,9 @@ +{ + "name": "example", + "devDependencies": { + "bun": "1.0.0" + }, + "scripts": { + "bunVersion": "echo Version && bun --version" + } +} diff --git a/src/test/resources/fixtures/bun/build.gradle b/src/test/resources/fixtures/bun/build.gradle new file mode 100644 index 00000000..455d7d77 --- /dev/null +++ b/src/test/resources/fixtures/bun/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'com.github.node-gradle.node' +} + +def changeInputs = isPropertyEnabled("changeInputs") + +node { + bunVersion = "1.0.3" + workDir = file('build/node') +} + +task test(type: BunTask) { + dependsOn bunInstall + bunCommand = changeInputs ? ['run', 'test'] : ['run'] + args = changeInputs ? [] : ['test'] + inputs.dir('node_modules') + inputs.file('package.json') + inputs.files('index.js', 'test.js') + outputs.upToDateWhen { + true + } +} + +task version(type: BunTask) { + dependsOn bunInstall + bunCommand = ["--version"] +} + +def isPropertyEnabled(String name) { + def provider = providers.systemProperty(name) + if (org.gradle.util.GradleVersion.current() >= org.gradle.util.GradleVersion.version("7.4")) { + return provider.isPresent() + } else { + return provider.forUseAtConfigurationTime().isPresent() + } +} diff --git a/src/test/resources/fixtures/bunx-env/build.gradle b/src/test/resources/fixtures/bunx-env/build.gradle new file mode 100644 index 00000000..7734ab14 --- /dev/null +++ b/src/test/resources/fixtures/bunx-env/build.gradle @@ -0,0 +1,76 @@ +plugins { + id "com.github.node-gradle.node" +} + +node { + workDir = file("build/node") +} + +task env(type: BunxTask) { + dependsOn bunInstall + command = "print-env" + args = ["PATH", "CUSTOM"] + outputs.upToDateWhen { + true + } +} + +task pwd(type: BunxTask) { + dependsOn bunInstall + command = "jseval" + /* This is cursed. + This used to work before we upgraded node+npm but now the backticks break, I tried to work around it and... + ["work..='", process.cwd(), "'"].join('') works, except on windows where it prints: + , process.cwd, + "work..='" + process.cwd() + "'" works, except on windows where it prints: + + process.cwd + + And surprising nobody at this point: + process.stdout.write("workingDirectory='"); process.stdout.write(process.cwd()); process.stdout.write("'\\n"); + Does work, except on windows, where it's parsed as: + process.stdout.write(workingDirectory='); process.stdout.write(process.cwd()); process.stdout.write('\\n); + + If someone manages to fix this, remove the @IgnoreIf from the NpxTask_integTest + */ + args = ["""console.log(["workingDirectory='", process.cwd(), "'"].join(''));"""] + outputs.upToDateWhen { + true + } +} + +task version(type: BunxTask) { + dependsOn bunInstall + command = "--version" +} + +if (isPropertyEnabled("customEnv")) { + def qualifier = "custom" + env.environment = [CUSTOM: "${qualifier} value"] +} + +if (isPropertyEnabled("ignoreExitValue")) { + env.ignoreExitValue = true +} + +if (isPropertyEnabled("notExistingCommand")) { + env.command = "notExistingCommand" +} + +if (isPropertyEnabled("customWorkingDir")) { + pwd.workingDir = file("${project.buildDir}/customWorkingDirectory/") +} + +if (isPropertyEnabled("outputFile")) { + def standardOutputFile = new File(buildDir, "standard-output.txt") + env.execOverrides { + standardOutput = new FileOutputStream(standardOutputFile) + } +} + +def isPropertyEnabled(String name) { + def provider = providers.systemProperty(name) + if (org.gradle.util.GradleVersion.current() >= org.gradle.util.GradleVersion.version("7.4")) { + return provider.isPresent() + } else { + return provider.forUseAtConfigurationTime().isPresent() + } +} diff --git a/src/test/resources/fixtures/bunx/build.gradle b/src/test/resources/fixtures/bunx/build.gradle new file mode 100644 index 00000000..8c71db03 --- /dev/null +++ b/src/test/resources/fixtures/bunx/build.gradle @@ -0,0 +1,61 @@ +plugins { + id "com.github.node-gradle.node" +} + +node { + bunVersion = "1.0.3" + workDir = file("build/node") +} + +// mocha is installed locally whereas eslint is not + +task lint(type: BunxTask) { + dependsOn bunInstall + command = "eslint@6.3.0" + args = ["src", "test"] + inputs.file(".eslintrc.yml") + inputs.dir("src") + inputs.dir("test") + outputs.upToDateWhen { + true + } +} + +task test(type: BunxTask) { + dependsOn lint + command = "mocha" + inputs.file("package.json") + inputs.dir("src") + inputs.dir("test") + outputs.upToDateWhen { + true + } +} + +task env(type: BunxTask) { + command = "print-env" + outputs.upToDateWhen { + true + } +} + +task cwd(type: BunxTask) { + command = "cwd" + outputs.upToDateWhen { + true + } +} + +if (isPropertyEnabled("changeInputs")) { + lint.args = ["src"] + test.command = "_mocha" +} + +def isPropertyEnabled(String name) { + def provider = providers.systemProperty(name) + if (org.gradle.util.GradleVersion.current() >= org.gradle.util.GradleVersion.version("7.4")) { + return provider.isPresent() + } else { + return provider.forUseAtConfigurationTime().isPresent() + } +}