70bf21e064
Handle Changesets / Handle Changesets (push) Waiting to run
Semgrep OSS scan / semgrep-oss (push) Waiting to run
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped
446 lines
13 KiB
TypeScript
446 lines
13 KiB
TypeScript
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { resolve } from "node:path";
|
|
import * as cliPackages from "@cloudflare/cli-shared-helpers/packages";
|
|
import { NpmPackageManager } from "@cloudflare/workers-utils";
|
|
import { runInTempDir } from "@cloudflare/workers-utils/test-helpers";
|
|
import { beforeEach, describe, it, vi } from "vitest";
|
|
import { Angular } from "../../src/frameworks/angular";
|
|
import { createMockContext } from "../helpers/mock-context";
|
|
import type { AutoConfigFrameworkPackageInfo } from "../../src/frameworks";
|
|
const context = createMockContext();
|
|
|
|
const BASE_OPTIONS = {
|
|
projectPath: process.cwd(),
|
|
workerName: "my-angular-app",
|
|
outputDir: "dist/my-angular-app/",
|
|
dryRun: false,
|
|
packageManager: NpmPackageManager,
|
|
isWorkspaceRoot: false,
|
|
context,
|
|
};
|
|
|
|
const ANGULAR_PACKAGE_INFO: AutoConfigFrameworkPackageInfo = {
|
|
name: "@angular/core",
|
|
minimumVersion: "19.0.0",
|
|
maximumKnownMajorVersion: "22",
|
|
};
|
|
|
|
async function mockAngularCoreVersion(version: string) {
|
|
const pkgDir = resolve("node_modules/@angular/core");
|
|
await mkdir(pkgDir, { recursive: true });
|
|
await writeFile(
|
|
resolve(pkgDir, "package.json"),
|
|
JSON.stringify({ name: "@angular/core", version })
|
|
);
|
|
}
|
|
|
|
describe("Angular framework configure()", () => {
|
|
runInTempDir();
|
|
|
|
let installSpy: ReturnType<typeof vi.spyOn>;
|
|
|
|
beforeEach(() => {
|
|
installSpy = vi
|
|
.spyOn(cliPackages, "installPackages")
|
|
.mockImplementation(async () => {});
|
|
});
|
|
|
|
describe("non-SSR (SPA) project", () => {
|
|
beforeEach(async () => {
|
|
/** Minimal angular.json for a non-SSR (SPA) project — no `ssr` field */
|
|
const NON_SSR_ANGULAR_JSON = {
|
|
projects: {
|
|
"my-angular-app": {
|
|
architect: {
|
|
build: {
|
|
options: {
|
|
browser: "src/main.ts",
|
|
tsConfig: "tsconfig.app.json",
|
|
assets: [{ glob: "**/*", input: "public" }],
|
|
styles: ["src/styles.css"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
await writeFile(
|
|
resolve("angular.json"),
|
|
JSON.stringify(NON_SSR_ANGULAR_JSON, null, 2)
|
|
);
|
|
await mkdir(resolve("src"), { recursive: true });
|
|
});
|
|
|
|
it("returns assets-only wranglerConfig", async ({ expect }) => {
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
const result = await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(result.wranglerConfig).toEqual({
|
|
assets: {
|
|
directory: "dist/my-angular-app/",
|
|
},
|
|
});
|
|
expect(result.wranglerConfig).not.toHaveProperty("main");
|
|
});
|
|
|
|
it("sets configurationDescription for SPA", async ({ expect }) => {
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(framework.configurationDescription).toBe(
|
|
"Configuring Angular SPA project (assets only)"
|
|
);
|
|
});
|
|
|
|
it("does not install additional dependencies", async ({ expect }) => {
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(installSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("does not create src/server.ts", async ({ expect }) => {
|
|
const { existsSync } = await import("node:fs");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(existsSync(resolve("src/server.ts"))).toBe(false);
|
|
});
|
|
|
|
it("does not modify angular.json", async ({ expect }) => {
|
|
const { readFileSync } = await import("node:fs");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
const angularJson = JSON.parse(
|
|
readFileSync(resolve("angular.json"), "utf8")
|
|
);
|
|
// outputMode and outputPath should not have been set
|
|
expect(
|
|
angularJson.projects["my-angular-app"].architect.build.options
|
|
.outputMode
|
|
).toBeUndefined();
|
|
expect(
|
|
angularJson.projects["my-angular-app"].architect.build.options
|
|
.outputPath
|
|
).toBeUndefined();
|
|
});
|
|
|
|
it("skips side effects in dryRun mode", async ({ expect }) => {
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
const result = await framework.configure({
|
|
...BASE_OPTIONS,
|
|
dryRun: true,
|
|
});
|
|
|
|
expect(result.wranglerConfig).toEqual({
|
|
assets: { directory: "dist/my-angular-app/" },
|
|
});
|
|
expect(installSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("project with ssr: false", () => {
|
|
beforeEach(async () => {
|
|
/** angular.json for a project with SSR explicitly set to false */
|
|
const SSR_FALSE_ANGULAR_JSON = {
|
|
projects: {
|
|
"my-angular-app": {
|
|
architect: {
|
|
build: {
|
|
options: {
|
|
browser: "src/main.ts",
|
|
tsConfig: "tsconfig.app.json",
|
|
assets: [],
|
|
styles: [],
|
|
ssr: false,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
await writeFile(
|
|
resolve("angular.json"),
|
|
JSON.stringify(SSR_FALSE_ANGULAR_JSON, null, 2)
|
|
);
|
|
await mkdir(resolve("src"), { recursive: true });
|
|
});
|
|
|
|
it("returns assets-only wranglerConfig when ssr is false", async ({
|
|
expect,
|
|
}) => {
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
const result = await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(result.wranglerConfig).toEqual({
|
|
assets: { directory: "dist/my-angular-app/" },
|
|
});
|
|
expect(result.wranglerConfig).not.toHaveProperty("main");
|
|
});
|
|
|
|
it("sets SPA configurationDescription when ssr is false", async ({
|
|
expect,
|
|
}) => {
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(framework.configurationDescription).toBe(
|
|
"Configuring Angular SPA project (assets only)"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("project with ssr: true (boolean shorthand)", () => {
|
|
beforeEach(async () => {
|
|
/** angular.json for a project with SSR enabled via the `true` boolean shorthand */
|
|
const SSR_TRUE_ANGULAR_JSON = {
|
|
projects: {
|
|
"my-angular-app": {
|
|
architect: {
|
|
build: {
|
|
options: {
|
|
browser: "src/main.ts",
|
|
tsConfig: "tsconfig.app.json",
|
|
assets: [],
|
|
styles: [],
|
|
ssr: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
await writeFile(
|
|
resolve("angular.json"),
|
|
JSON.stringify(SSR_TRUE_ANGULAR_JSON, null, 2)
|
|
);
|
|
await mkdir(resolve("src"), { recursive: true });
|
|
});
|
|
|
|
it("returns SSR wranglerConfig without crashing", async ({ expect }) => {
|
|
await mockAngularCoreVersion("21.0.0");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
framework.validateFrameworkVersion(
|
|
process.cwd(),
|
|
ANGULAR_PACKAGE_INFO,
|
|
context
|
|
);
|
|
const result = await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(result.wranglerConfig).toEqual({
|
|
main: "./dist/server/server.mjs",
|
|
assets: {
|
|
binding: "ASSETS",
|
|
directory: "dist/my-angular-app/browser",
|
|
},
|
|
});
|
|
});
|
|
|
|
it("sets experimentalPlatform in angular.json when ssr was true (Angular <22)", async ({
|
|
expect,
|
|
}) => {
|
|
const { readFileSync } = await import("node:fs");
|
|
await mockAngularCoreVersion("21.0.0");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
framework.validateFrameworkVersion(
|
|
process.cwd(),
|
|
ANGULAR_PACKAGE_INFO,
|
|
context
|
|
);
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
const angularJson = JSON.parse(
|
|
readFileSync(resolve("angular.json"), "utf8")
|
|
);
|
|
const options =
|
|
angularJson.projects["my-angular-app"].architect.build.options;
|
|
// The boolean `true` should have been promoted to an object
|
|
expect(typeof options.ssr).toBe("object");
|
|
expect(options.ssr.experimentalPlatform).toBe("neutral");
|
|
});
|
|
|
|
it("sets platform in angular.json when ssr was true (Angular >=22)", async ({
|
|
expect,
|
|
}) => {
|
|
const { readFileSync } = await import("node:fs");
|
|
await mockAngularCoreVersion("22.0.0");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
framework.validateFrameworkVersion(
|
|
process.cwd(),
|
|
ANGULAR_PACKAGE_INFO,
|
|
context
|
|
);
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
const angularJson = JSON.parse(
|
|
readFileSync(resolve("angular.json"), "utf8")
|
|
);
|
|
const options =
|
|
angularJson.projects["my-angular-app"].architect.build.options;
|
|
expect(typeof options.ssr).toBe("object");
|
|
expect(options.ssr.platform).toBe("neutral");
|
|
});
|
|
});
|
|
|
|
describe("SSR project", () => {
|
|
beforeEach(async () => {
|
|
/** angular.json for a project with SSR enabled (object value) */
|
|
const SSR_ANGULAR_JSON = {
|
|
projects: {
|
|
"my-angular-app": {
|
|
architect: {
|
|
build: {
|
|
options: {
|
|
browser: "src/main.ts",
|
|
tsConfig: "tsconfig.app.json",
|
|
assets: [],
|
|
styles: [],
|
|
ssr: {
|
|
entry: "src/server.ts",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
await writeFile(
|
|
resolve("angular.json"),
|
|
JSON.stringify(SSR_ANGULAR_JSON, null, 2)
|
|
);
|
|
await mkdir(resolve("src"), { recursive: true });
|
|
});
|
|
|
|
it("returns SSR wranglerConfig with main and assets", async ({
|
|
expect,
|
|
}) => {
|
|
await mockAngularCoreVersion("21.0.0");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
framework.validateFrameworkVersion(
|
|
process.cwd(),
|
|
ANGULAR_PACKAGE_INFO,
|
|
context
|
|
);
|
|
const result = await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(result.wranglerConfig).toEqual({
|
|
main: "./dist/server/server.mjs",
|
|
assets: {
|
|
binding: "ASSETS",
|
|
directory: "dist/my-angular-app/browser",
|
|
},
|
|
});
|
|
});
|
|
|
|
it("sets SSR configurationDescription", async ({ expect }) => {
|
|
await mockAngularCoreVersion("21.0.0");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
framework.validateFrameworkVersion(
|
|
process.cwd(),
|
|
ANGULAR_PACKAGE_INFO,
|
|
context
|
|
);
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(framework.configurationDescription).toBe(
|
|
"Configuring project for Angular"
|
|
);
|
|
});
|
|
|
|
it("sets experimentalPlatform in angular.json (Angular <22)", async ({
|
|
expect,
|
|
}) => {
|
|
const { readFileSync } = await import("node:fs");
|
|
await mockAngularCoreVersion("21.0.0");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
framework.validateFrameworkVersion(
|
|
process.cwd(),
|
|
ANGULAR_PACKAGE_INFO,
|
|
context
|
|
);
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
const angularJson = JSON.parse(
|
|
readFileSync(resolve("angular.json"), "utf8")
|
|
);
|
|
const options =
|
|
angularJson.projects["my-angular-app"].architect.build.options;
|
|
expect(options.outputMode).toBe("server");
|
|
expect(options.outputPath).toBe("dist");
|
|
expect(options.ssr.experimentalPlatform).toBe("neutral");
|
|
});
|
|
|
|
it("sets platform in angular.json (Angular >=22)", async ({ expect }) => {
|
|
const { readFileSync } = await import("node:fs");
|
|
await mockAngularCoreVersion("22.0.0");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
framework.validateFrameworkVersion(
|
|
process.cwd(),
|
|
ANGULAR_PACKAGE_INFO,
|
|
context
|
|
);
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
const angularJson = JSON.parse(
|
|
readFileSync(resolve("angular.json"), "utf8")
|
|
);
|
|
const options =
|
|
angularJson.projects["my-angular-app"].architect.build.options;
|
|
expect(options.outputMode).toBe("server");
|
|
expect(options.outputPath).toBe("dist");
|
|
expect(options.ssr.platform).toBe("neutral");
|
|
});
|
|
|
|
it("creates src/server.ts", async ({ expect }) => {
|
|
const { existsSync } = await import("node:fs");
|
|
await mockAngularCoreVersion("21.0.0");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
framework.validateFrameworkVersion(
|
|
process.cwd(),
|
|
ANGULAR_PACKAGE_INFO,
|
|
context
|
|
);
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(existsSync(resolve("src/server.ts"))).toBe(true);
|
|
});
|
|
|
|
it("installs additional dependencies", async ({ expect }) => {
|
|
await mockAngularCoreVersion("21.0.0");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
framework.validateFrameworkVersion(
|
|
process.cwd(),
|
|
ANGULAR_PACKAGE_INFO,
|
|
context
|
|
);
|
|
await framework.configure(BASE_OPTIONS);
|
|
|
|
expect(installSpy).toHaveBeenCalledWith(
|
|
NpmPackageManager.type,
|
|
["xhr2"],
|
|
expect.objectContaining({ dev: true })
|
|
);
|
|
});
|
|
|
|
it("skips side effects in dryRun mode", async ({ expect }) => {
|
|
const { existsSync } = await import("node:fs");
|
|
const framework = new Angular({ id: "angular", name: "Angular" });
|
|
const result = await framework.configure({
|
|
...BASE_OPTIONS,
|
|
dryRun: true,
|
|
});
|
|
|
|
// Config is still returned
|
|
expect(result.wranglerConfig).toHaveProperty("main");
|
|
// But side effects are skipped
|
|
expect(existsSync(resolve("src/server.ts"))).toBe(false);
|
|
expect(installSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|