Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 62 additions & 43 deletions lib/src/commands/pull.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { pull } from "./pull";
import httpClient from "../http/client";
import getHttpClient from "../http/client";
import { Component, TextItem } from "../http/types";
import appContext from "../utils/appContext";
import * as path from "path";
Expand All @@ -8,7 +8,13 @@ import * as os from "os";

jest.mock("../http/client");

const mockHttpClient = httpClient as jest.Mocked<typeof httpClient>;
// Create a mock client with a mock 'get' method
const mockHttpClient = {
get: jest.fn(),
};

// Make getHttpClient return the mock client
(getHttpClient as jest.Mock).mockReturnValue(mockHttpClient);

// Test data factories
const createMockTextItem = (overrides: Partial<TextItem> = {}) => ({
Expand All @@ -35,7 +41,7 @@ const createMockComponent = (overrides: Partial<Component> = {}) => ({
folderId: null,
variantId: null,
...overrides,
})
});

const createMockVariable = (overrides: any = {}) => ({
id: "var-1",
Expand All @@ -49,8 +55,16 @@ const createMockVariable = (overrides: any = {}) => ({
});

// Helper functions
const setupMocks = ({ textItems = [], components = [], variables = [] }: { textItems: TextItem[]; components?: Component[]; variables?: any[] }) => {
mockHttpClient.get.mockImplementation((url: string) => {
const setupMocks = ({
textItems = [],
components = [],
variables = [],
}: {
textItems: TextItem[];
components?: Component[];
variables?: any[];
}) => {
mockHttpClient.get.mockImplementation((url: string, config?: any) => {
if (url.includes("/v2/textItems")) {
return Promise.resolve({ data: textItems });
}
Expand Down Expand Up @@ -122,7 +136,7 @@ describe("pull command - end-to-end tests", () => {

const mockTextItem = createMockTextItem();
const mockComponent = createMockComponent();
setupMocks({ textItems: [mockTextItem], components: [mockComponent]});
setupMocks({ textItems: [mockTextItem], components: [mockComponent] });

// Set up appContext - this is what actually drives the test
appContext.setProjectConfig({
Expand All @@ -132,7 +146,7 @@ describe("pull command - end-to-end tests", () => {
outputs: [{ format: "json", outDir: outputDir }],
});

await pull();
await pull({});

// Verify rich text content was written
assertFileContainsText(
Expand All @@ -145,7 +159,7 @@ describe("pull command - end-to-end tests", () => {
path.join(outputDir, "components___base.json"),
"component-1",
"<p>Rich <strong>HTML</strong> content</p>"
)
);
});

it("should use plain text when richText is disabled at output level", async () => {
Expand All @@ -162,7 +176,7 @@ describe("pull command - end-to-end tests", () => {
outputs: [{ format: "json", outDir: outputDir, richText: false }],
});

await pull();
await pull({});

// Verify plain text content was written despite base config
assertFileContainsText(
Expand All @@ -189,7 +203,7 @@ describe("pull command - end-to-end tests", () => {
outputs: [{ format: "json", outDir: outputDir, richText: "html" }],
});

await pull();
await pull({});

// Verify rich text content was written
assertFileContainsText(
Expand All @@ -214,7 +228,7 @@ describe("pull command - end-to-end tests", () => {
],
});

await pull();
await pull({});

// Verify correct API call with filtered params
expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/textItems", {
Expand All @@ -238,7 +252,7 @@ describe("pull command - end-to-end tests", () => {
],
});

await pull();
await pull({});

// Verify correct API call with filtered params
expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/textItems", {
Expand All @@ -262,15 +276,14 @@ describe("pull command - end-to-end tests", () => {
],
});

await pull();
await pull({});

expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/components", {
params: {
filter:
'{}',
filter: "{}",
},
});
})
});

it("should filter components by folder at base level", async () => {
fs.mkdirSync(outputDir, { recursive: true });
Expand All @@ -287,15 +300,14 @@ describe("pull command - end-to-end tests", () => {
],
});

await pull();
await pull({});

expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/components", {
params: {
filter:
'{"folders":[{"id":"folder-1"}]}',
filter: '{"folders":[{"id":"folder-1"}]}',
},
});
})
});

it("should filter components by folder and variants at base level", async () => {
fs.mkdirSync(outputDir, { recursive: true });
Expand All @@ -313,15 +325,15 @@ describe("pull command - end-to-end tests", () => {
],
});

await pull();
await pull({});

expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/components", {
params: {
filter:
'{"folders":[{"id":"folder-1"}],"variants":[{"id":"variant-a"},{"id":"variant-b"}]}',
},
});
})
});

it("should filter components by folder at output level", async () => {
fs.mkdirSync(outputDir, { recursive: true });
Expand All @@ -335,21 +347,20 @@ describe("pull command - end-to-end tests", () => {
format: "json",
outDir: outputDir,
components: {
folders: [{ id: "folder-3" }]
}
folders: [{ id: "folder-3" }],
},
},
],
});

await pull();
await pull({});

expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/components", {
params: {
filter:
'{"folders":[{"id":"folder-3"}]}',
filter: '{"folders":[{"id":"folder-3"}]}',
},
});
})
});

it("should filter components by folder and variants at output level", async () => {
fs.mkdirSync(outputDir, { recursive: true });
Expand All @@ -363,22 +374,22 @@ describe("pull command - end-to-end tests", () => {
format: "json",
outDir: outputDir,
components: {
folders: [{ id: "folder-3" }]
folders: [{ id: "folder-3" }],
},
variants: [{ id: "variant-a" }, { id: "variant-b" }],
},
],
});

await pull();
await pull({});

expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/components", {
params: {
filter:
'{"folders":[{"id":"folder-3"}],"variants":[{"id":"variant-a"},{"id":"variant-b"}]}',
},
});
})
});

it("should filter projects at output level", async () => {
fs.mkdirSync(outputDir, { recursive: true });
Expand All @@ -394,7 +405,7 @@ describe("pull command - end-to-end tests", () => {
],
});

await pull();
await pull({});

// Verify correct API call with filtered params
expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/textItems", {
Expand All @@ -419,7 +430,7 @@ describe("pull command - end-to-end tests", () => {
],
});

await pull();
await pull({});

// Verify correct API call with filtered params
expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/textItems", {
Expand All @@ -443,18 +454,18 @@ describe("pull command - end-to-end tests", () => {
],
});

await pull();
await pull({});

// Verify correct API call with filtered params
expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/textItems", {
params: {
filter: "{\"projects\":[]}",
filter: '{"projects":[]}',
},
});
expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/variables")
expect(mockHttpClient.get).toHaveBeenCalledWith("/v2/variables");

// Components endpoint should not be called if not provided as source field
expect(mockHttpClient.get).toHaveBeenCalledTimes(2)
expect(mockHttpClient.get).toHaveBeenCalledTimes(2);
});
});

Expand Down Expand Up @@ -536,7 +547,7 @@ describe("pull command - end-to-end tests", () => {
variantId: null,
folderId: "folder-2",
}),
]
];

const componentsVariantA = [
createMockComponent({
Expand All @@ -549,7 +560,7 @@ describe("pull command - end-to-end tests", () => {
variantId: "variant-a",
folderId: "folder-1",
}),
]
];

const componentsVariantB = [
createMockComponent({
Expand All @@ -562,14 +573,22 @@ describe("pull command - end-to-end tests", () => {
variantId: "variant-b",
folderId: "folder-1",
}),
]
];

setupMocks({
textItems: [...baseTextItems, ...variantATextItems, ...variantBTextItems],
components: [...componentsBase, ...componentsVariantA, ...componentsVariantB],
textItems: [
...baseTextItems,
...variantATextItems,
...variantBTextItems,
],
components: [
...componentsBase,
...componentsVariantA,
...componentsVariantB,
],
});

await pull();
await pull({});

// Verify a file was created for each project and variant present in the (mocked) API response
assertFilesCreated(outputDir, [
Expand Down
5 changes: 3 additions & 2 deletions lib/src/commands/pull.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import appContext from "../utils/appContext";
import formatOutput from "../formatters";
import { CommandMetaFlags } from "../http/types";

export const pull = async () => {
export const pull = async (meta: CommandMetaFlags) => {
for (const output of appContext.selectedProjectConfigOutputs) {
await formatOutput(output, appContext.projectConfig);
await formatOutput(output, appContext.projectConfig, meta);
}
};
8 changes: 5 additions & 3 deletions lib/src/formatters/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { CommandMetaFlags } from "../http/types";
import { Output } from "../outputs";
import { ProjectConfigYAML } from "../services/projectConfig";
import JSONFormatter from "./json";

export default function handleOutput(
export default function formatOutput(
output: Output,
projectConfig: ProjectConfigYAML
projectConfig: ProjectConfigYAML,
meta: CommandMetaFlags
) {
switch (output.format) {
case "json":
return new JSONFormatter(output, projectConfig).format();
return new JSONFormatter(output, projectConfig, meta).format();
default:
throw new Error(`Unsupported output format: ${output}`);
}
Expand Down
11 changes: 8 additions & 3 deletions lib/src/formatters/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default class JSONFormatter extends applyMixins(
protected async fetchAPIData() {
const textItems = await this.fetchTextItems();
const components = await this.fetchComponents();
const variables = await fetchVariables();
const variables = await this.fetchVariables();

const variablesById = variables.reduce((acc, variable) => {
acc[variable.id] = variable;
Expand Down Expand Up @@ -137,6 +137,7 @@ export default class JSONFormatter extends applyMixins(
params.richText = this.output.richText;
}


return params;
}

Expand All @@ -149,7 +150,7 @@ export default class JSONFormatter extends applyMixins(
private async fetchTextItems() {
if (!this.projectConfig.projects && !this.output.projects) return [];

return await fetchText(this.generateQueryParams("textItem"));
return await fetchText(this.generateQueryParams("textItem"), this.meta);
}

/**
Expand All @@ -161,6 +162,10 @@ export default class JSONFormatter extends applyMixins(
private async fetchComponents() {
if (!this.projectConfig.components && !this.output.components) return [];

return await fetchComponents(this.generateQueryParams("component"));
return await fetchComponents(this.generateQueryParams("component"), this.meta);
}

private async fetchVariables() {
return await fetchVariables(this.meta);
}
}
Loading