Skip to content
Open
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
6 changes: 3 additions & 3 deletions lib/src/formatters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CommandMetaFlags } from "../http/types";
import { Output } from "../outputs";
import { ProjectConfigYAML } from "../services/projectConfig";
import AndroidXMLFormatter from "./android";
import ICUFormatter from "./icu";
import JSONICUFormatter from "./jsonICU";
import IOSStringsFormatter from "./iosStrings";
import IOSStringsDictFormatter from "./iosStringsDict";
import JSONFormatter from "./json";
Expand All @@ -21,8 +21,8 @@ export default function formatOutput(
return new IOSStringsFormatter(output, projectConfig, meta).format();
case "ios-stringsdict":
return new IOSStringsDictFormatter(output, projectConfig, meta).format();
case "icu":
return new ICUFormatter(output, projectConfig, meta).format();
case "json_icu":
return new JSONICUFormatter(output, projectConfig, meta).format();
default:
throw new Error(`Unsupported output format: ${output}`);
}
Expand Down
12 changes: 11 additions & 1 deletion lib/src/formatters/iosStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
ExportTextItemsStringResponse,
PullQueryParams,
} from "../http/types";
import OutputFile from "./shared/fileTypes/OutputFile";

export default class IOSStringsFormatter extends BaseExportFormatter<
IOSStringsOutputFile<{ variantId: string }>,
ExportTextItemsStringResponse,
Expand All @@ -19,9 +21,17 @@ export default class IOSStringsFormatter extends BaseExportFormatter<
): void {
this.outputFiles[fileName] ??= new IOSStringsOutputFile({
filename: fileName,
path: this.outDir,
path: this.getLocalesPath(variantId),
metadata: { variantId: variantId || "base" },
content: content,
});
}

protected async writeFiles(files: OutputFile[]): Promise<void> {
if (this.projectConfig.iosLocales) {
const swiftDriverFile = await this.getSwiftDriverFile();
files.push(swiftDriverFile);
}
await super.writeFiles([...files]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need the spread here

}
}
11 changes: 10 additions & 1 deletion lib/src/formatters/iosStringsDict.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ExportTextItemsStringResponse,
PullQueryParams,
} from "../http/types";
import OutputFile from "./shared/fileTypes/OutputFile";
export default class IOSStringsDictFormatter extends BaseExportFormatter<
IOSStringsDictOutputFile<{ variantId: string }>,
ExportTextItemsStringResponse,
Expand All @@ -19,9 +20,17 @@ export default class IOSStringsDictFormatter extends BaseExportFormatter<
): void {
this.outputFiles[fileName] ??= new IOSStringsDictOutputFile({
filename: fileName,
path: this.outDir,
path: this.getLocalesPath(variantId),
metadata: { variantId: variantId || "base" },
content: content,
});
}

protected async writeFiles(files: OutputFile[]): Promise<void> {
if (this.projectConfig.iosLocales) {
const swiftDriverFile = await this.getSwiftDriverFile();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we call writeFiles for both iosStringsDict and iosStrings. Does that mean we are generating the driver file twice?

files.push(swiftDriverFile);
}
await super.writeFiles(files);
}
}
4 changes: 2 additions & 2 deletions lib/src/formatters/icu.ts → lib/src/formatters/jsonICU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import {
PullQueryParams,
} from "../http/types";

export default class ICUFormatter extends BaseExportFormatter<
export default class JSONICUFormatter extends BaseExportFormatter<
ICUOutputFile<{ variantId: string }>,
ExportTextItemsJSONResponse,
ExportComponentsJSONResponse
> {
protected exportFormat: PullQueryParams["format"] = "icu";
protected exportFormat: PullQueryParams["format"] = "json_icu";

protected createOutputFile(
fileName: string,
Expand Down
1 change: 1 addition & 0 deletions lib/src/formatters/shared/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,3 +399,4 @@ describe("BaseFormatter", () => {
});
});
});

2 changes: 1 addition & 1 deletion lib/src/formatters/shared/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default class BaseFormatter<OutputFileType, APIDataType = unknown> {
await this.writeFiles(files);
}

private async writeFiles(files: OutputFile[]): Promise<void> {
protected async writeFiles(files: OutputFile[]): Promise<void> {
await Promise.all(
files.map((file) =>
writeFile(file.fullPath, file.formattedContent).then(() => {
Expand Down
258 changes: 258 additions & 0 deletions lib/src/formatters/shared/baseExport.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ import fetchText from "../../http/textItems";
import fetchComponents from "../../http/components";
import fetchProjects from "../../http/projects";
import fetchVariants from "../../http/variants";
import generateSwiftDriver from "../../http/cli";
import appContext from "../../utils/appContext";
import BaseExportFormatter from "./baseExport";

jest.mock("../../http/textItems");
jest.mock("../../http/components");
jest.mock("../../http/projects");
jest.mock("../../http/variants");
jest.mock("../../http/cli");
jest.mock("../../utils/appContext", () => ({
__esModule: true,
default: {
outDir: "/mock/app/context/outDir",
},
}));

const mockFetchText = fetchText as jest.MockedFunction<typeof fetchText>;
const mockFetchComponents = fetchComponents as jest.MockedFunction<
Expand All @@ -26,6 +35,9 @@ const mockFetchProjects = fetchProjects as jest.MockedFunction<
const mockFetchVariants = fetchVariants as jest.MockedFunction<
typeof fetchVariants
>;
const mockGenerateSwiftDriver = generateSwiftDriver as jest.MockedFunction<
typeof generateSwiftDriver
>;

// fake test class to expose private methods
// @ts-ignore
Expand Down Expand Up @@ -57,6 +69,14 @@ class TestBaseExportFormatter extends BaseExportFormatter {
public async fetchComponentsMap() {
return super["fetchComponentsMap"]();
}

public getLocalesPath(variantId: string) {
return super.getLocalesPath(variantId);
}

public async getSwiftDriverFile() {
return super.getSwiftDriverFile();
}
}

describe("BaseExportFormatter", () => {
Expand Down Expand Up @@ -438,4 +458,242 @@ describe("BaseExportFormatter", () => {
);
});
});

/***********************************************************
* getLocalesPath
***********************************************************/
Comment on lines +462 to +464

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this comment is unnecessary if we have the describe block

describe("getLocalesPath", () => {
it("should return output outDir when iosLocales is not configured", () => {
const projectConfig = createMockProjectConfig({
iosLocales: undefined,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test for iosLocales: []

});
const output = createMockOutput({ outDir: "/test/output" });
// @ts-ignore
const formatter = new TestBaseExportFormatter(
output,
projectConfig,
createMockMeta()
);

const result = formatter.getLocalesPath("base");

expect(result).toBe("/test/output");
});

it("should return locale path when iosLocales is configured and variantId matches", () => {
const projectConfig = createMockProjectConfig({
iosLocales: [{ base: "en" }, { variant1: "es" }, { variant2: "fr" }],
});
const output = createMockOutput({ outDir: "/test/output" });
// @ts-ignore
const formatter = new TestBaseExportFormatter(
output,
projectConfig,
createMockMeta()
);

const result = formatter.getLocalesPath("variant1");

expect(result).toBe("/mock/app/context/outDir/es.lproj");
});

it("should return output's outDir when iosLocales is configured but variantId does not exist in iosLocales map", () => {
const projectConfig = createMockProjectConfig({
iosLocales: [{ base: "en" }, { variant1: "es" }],
});
const output = createMockOutput({ outDir: "/test/output" });
// @ts-ignore
const formatter = new TestBaseExportFormatter(
output,
projectConfig,
createMockMeta()
);

const result = formatter.getLocalesPath("variant2");

expect(result).toBe("/test/output");
});

it("should return locale path for base variant when configured", () => {
const projectConfig = createMockProjectConfig({
iosLocales: [{ base: "en" }, { variant1: "es" }],
});
const output = createMockOutput({ outDir: "/test/output" });
// @ts-ignore
const formatter = new TestBaseExportFormatter(
output,
projectConfig,
createMockMeta()
);

const result = formatter.getLocalesPath("base");

expect(result).toBe("/mock/app/context/outDir/en.lproj");
});
});

/***********************************************************
* getSwiftDriverFile
***********************************************************/
describe("getSwiftDriverFile", () => {
it("should generate Swift driver file with components folders from projectConfig", async () => {
const projectConfig = createMockProjectConfig({
components: {
folders: [{ id: "folder1" }, { id: "folder2" }],
},
projects: [{ id: "project1" }],
});
const output = createMockOutput();
// @ts-ignore
const formatter = new TestBaseExportFormatter(
output,
projectConfig,
createMockMeta()
);

const mockSwiftDriver = "import Foundation\nclass Ditto { }";
mockGenerateSwiftDriver.mockResolvedValue(mockSwiftDriver);

const result = await formatter.getSwiftDriverFile();

expect(mockGenerateSwiftDriver).toHaveBeenCalledWith(
{
components: {
folders: [{ id: "folder1" }, { id: "folder2" }],
},
projects: [{ id: "project1" }],
},
{}
);
expect(result.filename).toBe("Ditto");
expect(result.path).toBe("/mock/app/context/outDir");
expect(result.content).toBe(mockSwiftDriver);
});

it("should generate Swift driver file with components folders from output", async () => {
const projectConfig = createMockProjectConfig({
components: {
folders: [{ id: "config-folder" }],
},
});
const output = createMockOutput({
components: {
folders: [{ id: "output-folder1" }, { id: "output-folder2" }],
},
});
// @ts-ignore
const formatter = new TestBaseExportFormatter(
output,
projectConfig,
createMockMeta()
);

const mockSwiftDriver = "import Foundation\nclass Ditto { }";
mockGenerateSwiftDriver.mockResolvedValue(mockSwiftDriver);

const result = await formatter.getSwiftDriverFile();

expect(mockGenerateSwiftDriver).toHaveBeenCalledWith(
{
components: {
folders: [{ id: "output-folder1" }, { id: "output-folder2" }],
},
projects: [],
},
{}
);
expect(result.filename).toBe("Ditto");
expect(result.path).toBe("/mock/app/context/outDir");
expect(result.content).toBe(mockSwiftDriver);
});

it("should generate Swift driver file with projects from output", async () => {
const projectConfig = createMockProjectConfig({
projects: [{ id: "config-project" }],
components: undefined,
});
const output = createMockOutput({
projects: [{ id: "output-project1" }, { id: "output-project2" }],
});
// @ts-ignore
const formatter = new TestBaseExportFormatter(
output,
projectConfig,
createMockMeta()
);

const mockSwiftDriver = "import Foundation\nclass Ditto { }";
mockGenerateSwiftDriver.mockResolvedValue(mockSwiftDriver);

const result = await formatter.getSwiftDriverFile();

expect(mockGenerateSwiftDriver).toHaveBeenCalledWith(
{
projects: [{ id: "output-project1" }, { id: "output-project2" }],
},
{}
);
expect(result.filename).toBe("Ditto");
expect(result.path).toBe("/mock/app/context/outDir");
});

it("should generate Swift driver file with empty projects array when not configured", async () => {
const projectConfig = createMockProjectConfig({
projects: [],
components: {
folders: [],
},
});
const output = createMockOutput();
// @ts-ignore
const formatter = new TestBaseExportFormatter(
output,
projectConfig,
createMockMeta()
);

const mockSwiftDriver = "import Foundation\nclass Ditto { }";
mockGenerateSwiftDriver.mockResolvedValue(mockSwiftDriver);

const result = await formatter.getSwiftDriverFile();

expect(mockGenerateSwiftDriver).toHaveBeenCalledWith(
{
projects: [],
components: {
folders: [],
},
},
{}
);
expect(result.filename).toBe("Ditto");
expect(result.path).toBe("/mock/app/context/outDir");
});

it("should not include components in filters when components not configured", async () => {
const projectConfig = createMockProjectConfig({
components: undefined,
projects: [{ id: "project1" }],
});
const output = createMockOutput();
// @ts-ignore
const formatter = new TestBaseExportFormatter(
output,
projectConfig,
createMockMeta()
);

const mockSwiftDriver = "import Foundation\nclass Ditto { }";
mockGenerateSwiftDriver.mockResolvedValue(mockSwiftDriver);

await formatter.getSwiftDriverFile();

expect(mockGenerateSwiftDriver).toHaveBeenCalledWith(
{
projects: [{ id: "project1" }],
},
{}
);
});
});
});
Loading