@dittowords/cli 4.4.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/actions/install-node-dependencies/action.yml +24 -0
- package/.github/workflows/required-checks.yml +24 -0
- package/__mocks__/fs.js +2 -0
- package/bin/__mocks__/api.js +48 -0
- package/bin/__mocks__/api.js.map +1 -0
- package/bin/config.js +6 -4
- package/bin/config.js.map +1 -1
- package/bin/config.test.js +4 -3
- package/bin/config.test.js.map +1 -1
- package/bin/consts.js +19 -29
- package/bin/consts.js.map +1 -1
- package/bin/generate-suggestions.js +68 -58
- package/bin/generate-suggestions.js.map +1 -1
- package/bin/generate-suggestions.test.js +24 -13
- package/bin/generate-suggestions.test.js.map +1 -1
- package/bin/http/__mocks__/fetchComponentFolders.js +71 -0
- package/bin/http/__mocks__/fetchComponentFolders.js.map +1 -0
- package/bin/http/__mocks__/fetchComponents.js +73 -0
- package/bin/http/__mocks__/fetchComponents.js.map +1 -0
- package/bin/http/__mocks__/fetchVariants.js +71 -0
- package/bin/http/__mocks__/fetchVariants.js.map +1 -0
- package/bin/http/fetchComponentFolders.js +4 -4
- package/bin/http/fetchComponentFolders.js.map +1 -1
- package/bin/http/fetchComponents.js +4 -4
- package/bin/http/fetchComponents.js.map +1 -1
- package/bin/http/fetchVariants.js +2 -2
- package/bin/http/fetchVariants.js.map +1 -1
- package/bin/http/http.test.js +159 -0
- package/bin/http/http.test.js.map +1 -0
- package/bin/http/importComponents.js +9 -2
- package/bin/http/importComponents.js.map +1 -1
- package/bin/init/project.test.js +5 -28
- package/bin/init/project.test.js.map +1 -1
- package/bin/init/token.js +72 -27
- package/bin/init/token.js.map +1 -1
- package/bin/init/token.test.js +87 -9
- package/bin/init/token.test.js.map +1 -1
- package/bin/pull-lib.test.js +379 -0
- package/bin/pull-lib.test.js.map +1 -0
- package/bin/pull.js +13 -5
- package/bin/pull.js.map +1 -1
- package/bin/pull.test.js +100 -289
- package/bin/pull.test.js.map +1 -1
- package/bin/replace.js +22 -6
- package/bin/replace.js.map +1 -1
- package/bin/replace.test.js +53 -11
- package/bin/replace.test.js.map +1 -1
- package/bin/types.js +2 -2
- package/bin/types.js.map +1 -1
- package/bin/utils/determineModuleType.js +6 -7
- package/bin/utils/determineModuleType.js.map +1 -1
- package/bin/utils/determineModuleType.test.js +60 -0
- package/bin/utils/determineModuleType.test.js.map +1 -0
- package/bin/utils/getSelectedProjects.js +5 -5
- package/bin/utils/getSelectedProjects.js.map +1 -1
- package/bin/utils/quit.js +3 -3
- package/bin/utils/quit.js.map +1 -1
- package/jest.config.ts +16 -0
- package/lib/__mocks__/api.ts +12 -0
- package/lib/config.test.ts +3 -1
- package/lib/config.ts +4 -2
- package/lib/consts.ts +19 -17
- package/lib/generate-suggestions.test.ts +23 -11
- package/lib/generate-suggestions.ts +89 -79
- package/lib/http/__mocks__/fetchComponentFolders.ts +23 -0
- package/lib/http/__mocks__/fetchComponents.ts +24 -0
- package/lib/http/__mocks__/fetchVariants.ts +21 -0
- package/lib/http/fetchComponentFolders.ts +6 -4
- package/lib/http/fetchComponents.ts +5 -3
- package/lib/http/fetchVariants.ts +14 -3
- package/lib/http/http.test.ts +122 -0
- package/lib/http/importComponents.ts +8 -0
- package/lib/init/project.test.ts +4 -27
- package/lib/init/token.test.ts +55 -7
- package/lib/init/token.ts +76 -27
- package/lib/pull-lib.test.ts +367 -0
- package/lib/pull.test.ts +97 -310
- package/lib/pull.ts +11 -3
- package/lib/replace.test.ts +46 -10
- package/lib/replace.ts +20 -3
- package/lib/types.ts +5 -0
- package/lib/utils/determineModuleType.test.ts +48 -0
- package/lib/utils/determineModuleType.ts +4 -6
- package/lib/utils/getSelectedProjects.ts +3 -3
- package/lib/utils/quit.ts +1 -1
- package/package.json +4 -3
- package/jest.config.js +0 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fs from "fs
|
|
1
|
+
import fs from "fs";
|
|
2
2
|
import glob from "glob";
|
|
3
3
|
import { parse } from "@babel/parser";
|
|
4
4
|
import traverse from "@babel/traverse";
|
|
@@ -43,11 +43,14 @@ async function generateSuggestions(flags: {
|
|
|
43
43
|
console.log(JSON.stringify(results, null, 2));
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
async function findComponentsInJSXFiles(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
async function findComponentsInJSXFiles(
|
|
47
|
+
params: (
|
|
48
|
+
| { directory: string; files?: string[] }
|
|
49
|
+
| { files: string[]; directory?: string }
|
|
50
|
+
) & {
|
|
51
|
+
components: FetchComponentResponse;
|
|
52
|
+
}
|
|
53
|
+
): Promise<{ [apiId: string]: Result }> {
|
|
51
54
|
const result: { [apiId: string]: Result } = {};
|
|
52
55
|
const files =
|
|
53
56
|
params.files ||
|
|
@@ -57,83 +60,90 @@ async function findComponentsInJSXFiles(params: {
|
|
|
57
60
|
|
|
58
61
|
const promises: Promise<any>[] = [];
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
fs.readFile(file, "utf-8"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
result[compApiId] = {
|
|
76
|
-
apiId: compApiId,
|
|
77
|
-
...component,
|
|
78
|
-
occurrences: {},
|
|
79
|
-
};
|
|
80
|
-
}
|
|
63
|
+
async function handleFile(file: string) {
|
|
64
|
+
const code = await new Promise<string>((resolve, reject) =>
|
|
65
|
+
fs.readFile(file, "utf-8", (err, data) => {
|
|
66
|
+
if (err) {
|
|
67
|
+
reject(err);
|
|
68
|
+
} else {
|
|
69
|
+
resolve(data);
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const ast = parse(code, {
|
|
75
|
+
sourceType: "module",
|
|
76
|
+
plugins: ["jsx", "typescript"],
|
|
77
|
+
});
|
|
81
78
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
// Initialize the occurrences array if it doesn't exist
|
|
117
|
-
if (!result[compApiId]["occurrences"][file]) {
|
|
118
|
-
result[compApiId]["occurrences"][file] = [];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
result[compApiId]["occurrences"][file].push({
|
|
122
|
-
lineNumber,
|
|
123
|
-
preview,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
79
|
+
traverse(ast, {
|
|
80
|
+
JSXText(path) {
|
|
81
|
+
for (const [compApiId, component] of Object.entries(
|
|
82
|
+
params.components
|
|
83
|
+
)) {
|
|
84
|
+
// If we haven't seen this component before, add it to the result
|
|
85
|
+
if (!result[compApiId]) {
|
|
86
|
+
result[compApiId] = {
|
|
87
|
+
apiId: compApiId,
|
|
88
|
+
...component,
|
|
89
|
+
occurrences: {},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
// Skip white space lines
|
|
95
|
+
!/^\s*$/.test(path.node.value) &&
|
|
96
|
+
!/^\s*$/.test(component.text) &&
|
|
97
|
+
path.node.value.includes(component.text)
|
|
98
|
+
) {
|
|
99
|
+
// Escape all special characters from the text so we can use it in a regex
|
|
100
|
+
const escapedText = component.text.replace(
|
|
101
|
+
/[.*+?^${}()|[\]\\]/g,
|
|
102
|
+
"\\$&"
|
|
103
|
+
);
|
|
104
|
+
const regex = new RegExp(escapedText, "g");
|
|
105
|
+
let match;
|
|
106
|
+
while ((match = regex.exec(path.node.value)) !== null) {
|
|
107
|
+
const lines = path.node.value.slice(0, match.index).split("\n");
|
|
108
|
+
|
|
109
|
+
if (!path.node.loc) {
|
|
110
|
+
continue;
|
|
126
111
|
}
|
|
127
112
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
113
|
+
const lineNumber = path.node.loc.start.line + lines.length - 1;
|
|
114
|
+
|
|
115
|
+
const codeLines = code.split("\n");
|
|
116
|
+
const line = codeLines[lineNumber - 1];
|
|
117
|
+
const preview = replaceAt(
|
|
118
|
+
line,
|
|
119
|
+
match.index,
|
|
120
|
+
component.text,
|
|
121
|
+
`${component.text}`
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Initialize the occurrences array if it doesn't exist
|
|
125
|
+
if (!result[compApiId]["occurrences"][file]) {
|
|
126
|
+
result[compApiId]["occurrences"][file] = [];
|
|
131
127
|
}
|
|
128
|
+
|
|
129
|
+
result[compApiId]["occurrences"][file].push({
|
|
130
|
+
lineNumber,
|
|
131
|
+
preview,
|
|
132
|
+
});
|
|
132
133
|
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Remove from result if no occurrences were found
|
|
137
|
+
if (Object.keys(result[compApiId]["occurrences"]).length === 0) {
|
|
138
|
+
delete result[compApiId];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (const file of files) {
|
|
146
|
+
promises.push(handleFile(file));
|
|
137
147
|
}
|
|
138
148
|
|
|
139
149
|
await Promise.all(promises);
|
|
@@ -145,7 +155,7 @@ function replaceAt(
|
|
|
145
155
|
str: string,
|
|
146
156
|
index: number,
|
|
147
157
|
searchString: string,
|
|
148
|
-
replacement: string
|
|
158
|
+
replacement: string
|
|
149
159
|
) {
|
|
150
160
|
return (
|
|
151
161
|
str.substring(0, index) +
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { FetchComponentFoldersResponse } from "../fetchComponentFolders";
|
|
2
|
+
|
|
3
|
+
export const MOCK_COMPONENT_FOLDERS: FetchComponentFoldersResponse = {
|
|
4
|
+
example_folder: "Example Folder",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const MOCK_COMPONENT_FOLDERS_WITH_SAMPLE_DATA: FetchComponentFoldersResponse =
|
|
8
|
+
{
|
|
9
|
+
example_folder: "Example Folder",
|
|
10
|
+
example_folder_sample: "Sample Example Folder",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export async function fetchComponentFolders(
|
|
14
|
+
_options: {
|
|
15
|
+
showSampleData?: boolean;
|
|
16
|
+
} = {}
|
|
17
|
+
): Promise<FetchComponentFoldersResponse> {
|
|
18
|
+
if (_options.showSampleData) {
|
|
19
|
+
return MOCK_COMPONENT_FOLDERS_WITH_SAMPLE_DATA;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return MOCK_COMPONENT_FOLDERS;
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FetchComponentResponse } from "../fetchComponents";
|
|
2
|
+
|
|
3
|
+
export const MOCK_COMPONENTS_RESPONSE: FetchComponentResponse = {
|
|
4
|
+
"component-1": {
|
|
5
|
+
name: "Example Component 1",
|
|
6
|
+
text: "This is example component text.",
|
|
7
|
+
status: "NONE",
|
|
8
|
+
folder: null,
|
|
9
|
+
},
|
|
10
|
+
"component-2": {
|
|
11
|
+
name: "Example Component 2",
|
|
12
|
+
text: "This is example component text.",
|
|
13
|
+
status: "NONE",
|
|
14
|
+
folder: null,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export async function fetchComponents(
|
|
19
|
+
_options: {
|
|
20
|
+
componentFolder?: string;
|
|
21
|
+
} = {}
|
|
22
|
+
): Promise<FetchComponentResponse> {
|
|
23
|
+
return MOCK_COMPONENTS_RESPONSE;
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { IVariant } from "../fetchVariants";
|
|
2
|
+
|
|
3
|
+
export const MOCK_VARIANTS_RESPONSE: IVariant[] = [
|
|
4
|
+
{
|
|
5
|
+
name: "Example Variant 1",
|
|
6
|
+
description: "This is example variant 1.",
|
|
7
|
+
apiID: "example-variant-1",
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
name: "Example Variant 2",
|
|
11
|
+
description: "This is example variant 2.",
|
|
12
|
+
apiID: "example-variant-2",
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export async function fetchVariants(
|
|
17
|
+
_source: any,
|
|
18
|
+
_options: any = {}
|
|
19
|
+
): Promise<IVariant[] | null> {
|
|
20
|
+
return MOCK_VARIANTS_RESPONSE;
|
|
21
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { createApiClient } from "../api";
|
|
2
2
|
|
|
3
|
-
interface FetchComponentFoldersResponse {
|
|
3
|
+
export interface FetchComponentFoldersResponse {
|
|
4
4
|
[id: string]: string;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export async function fetchComponentFolders(
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
export async function fetchComponentFolders(
|
|
8
|
+
options: {
|
|
9
|
+
showSampleData?: boolean;
|
|
10
|
+
} = {}
|
|
11
|
+
): Promise<FetchComponentFoldersResponse> {
|
|
10
12
|
const api = createApiClient();
|
|
11
13
|
|
|
12
14
|
let url = "/v1/component-folders";
|
|
@@ -11,9 +11,11 @@ export interface FetchComponentResponse {
|
|
|
11
11
|
[compApiId: string]: FetchComponentResponseComponent;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export async function fetchComponents(
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
export async function fetchComponents(
|
|
15
|
+
options: {
|
|
16
|
+
componentFolder?: string;
|
|
17
|
+
} = {}
|
|
18
|
+
): Promise<FetchComponentResponse> {
|
|
17
19
|
const api = createApiClient();
|
|
18
20
|
|
|
19
21
|
if (options.componentFolder) {
|
|
@@ -3,10 +3,21 @@ import { createApiClient } from "../api";
|
|
|
3
3
|
import { PullOptions } from "../pull";
|
|
4
4
|
import { SourceInformation } from "../types";
|
|
5
5
|
|
|
6
|
+
export interface IVariant {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
apiID: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type SourceArg = Pick<
|
|
13
|
+
SourceInformation,
|
|
14
|
+
"shouldFetchComponentLibrary" | "validProjects" | "variants"
|
|
15
|
+
>;
|
|
16
|
+
|
|
6
17
|
export async function fetchVariants(
|
|
7
|
-
source:
|
|
18
|
+
source: SourceArg,
|
|
8
19
|
options: PullOptions = {}
|
|
9
|
-
) {
|
|
20
|
+
): Promise<IVariant[] | null> {
|
|
10
21
|
const api = createApiClient();
|
|
11
22
|
if (!source.variants) {
|
|
12
23
|
return null;
|
|
@@ -25,7 +36,7 @@ export async function fetchVariants(
|
|
|
25
36
|
config.params.projectIds = validProjects.map(({ id }) => id);
|
|
26
37
|
}
|
|
27
38
|
|
|
28
|
-
const { data } = await api.get<
|
|
39
|
+
const { data } = await api.get<IVariant[]>("/v1/variants", config);
|
|
29
40
|
|
|
30
41
|
return data;
|
|
31
42
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { fetchComponentFolders } from "./fetchComponentFolders";
|
|
2
|
+
import { fetchComponents } from "./fetchComponents";
|
|
3
|
+
import { fetchVariants } from "./fetchVariants";
|
|
4
|
+
import { importComponents } from "./importComponents";
|
|
5
|
+
import { jest } from "@jest/globals";
|
|
6
|
+
import axios from "axios";
|
|
7
|
+
import { vol } from "memfs";
|
|
8
|
+
|
|
9
|
+
jest.mock("../api");
|
|
10
|
+
jest.mock("fs");
|
|
11
|
+
|
|
12
|
+
const axiosMocked = jest.mocked(axios);
|
|
13
|
+
|
|
14
|
+
describe("fetchComponentFolders", () => {
|
|
15
|
+
it("fetches component folders without error", async () => {
|
|
16
|
+
axiosMocked.get.mockResolvedValueOnce({
|
|
17
|
+
data: { "folder-id": "folder-name" },
|
|
18
|
+
});
|
|
19
|
+
const result = await fetchComponentFolders();
|
|
20
|
+
expect(result["folder-id"]).toBe("folder-name");
|
|
21
|
+
});
|
|
22
|
+
it("supports showSampleData option", async () => {
|
|
23
|
+
axiosMocked.get.mockResolvedValueOnce({
|
|
24
|
+
data: { "folder-id": "folder-name" },
|
|
25
|
+
});
|
|
26
|
+
const result = await fetchComponentFolders({ showSampleData: true });
|
|
27
|
+
expect(result["folder-id"]).toBe("folder-name");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("fetchComponents", () => {
|
|
32
|
+
it("fetches components without error", async () => {
|
|
33
|
+
axiosMocked.get.mockResolvedValueOnce({
|
|
34
|
+
data: {
|
|
35
|
+
"component-id": {
|
|
36
|
+
name: "component-name",
|
|
37
|
+
text: "component-text",
|
|
38
|
+
status: "NONE",
|
|
39
|
+
folder: null,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const result = await fetchComponents();
|
|
44
|
+
expect(result["component-id"]).toBeDefined();
|
|
45
|
+
expect(result["component-id"].name).toBe("component-name");
|
|
46
|
+
expect(result["component-id"].text).toBe("component-text");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("fetchVariants", () => {
|
|
51
|
+
it("fetches variants without error", async () => {
|
|
52
|
+
axiosMocked.get.mockResolvedValueOnce({
|
|
53
|
+
data: [
|
|
54
|
+
{
|
|
55
|
+
name: "variant-name",
|
|
56
|
+
description: "variant-description",
|
|
57
|
+
apiID: "variant-api-id",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
const result = await fetchVariants({
|
|
62
|
+
shouldFetchComponentLibrary: true,
|
|
63
|
+
validProjects: [],
|
|
64
|
+
variants: true,
|
|
65
|
+
});
|
|
66
|
+
expect(result).toBeTruthy();
|
|
67
|
+
expect(result![0]).toBeDefined();
|
|
68
|
+
expect(result![0].name).toBe("variant-name");
|
|
69
|
+
expect(result![0].description).toBe("variant-description");
|
|
70
|
+
expect(result![0].apiID).toBe("variant-api-id");
|
|
71
|
+
});
|
|
72
|
+
it("returns null if `variants` isn't in config", async () => {
|
|
73
|
+
const result = await fetchVariants({
|
|
74
|
+
shouldFetchComponentLibrary: true,
|
|
75
|
+
validProjects: [],
|
|
76
|
+
variants: false,
|
|
77
|
+
});
|
|
78
|
+
expect(result).toBe(null);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("importComponents", () => {
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
vol.reset();
|
|
85
|
+
});
|
|
86
|
+
it("imports components from existing file without error", async () => {
|
|
87
|
+
vol.fromJSON({
|
|
88
|
+
"/file.csv": "id,name,text\n1,one1,not empty\n2,two1,empty",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
axiosMocked.mockResolvedValueOnce({
|
|
92
|
+
data: {
|
|
93
|
+
componentsInserted: 2,
|
|
94
|
+
firstImportedId: "1",
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const result = await importComponents("/file.csv", {
|
|
99
|
+
csvColumnMapping: {
|
|
100
|
+
name: "name",
|
|
101
|
+
text: 2,
|
|
102
|
+
componentId: 0,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(result.componentsInserted).toBe(2);
|
|
107
|
+
expect(result.firstImportedId).toBe("1");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("returns null firstImportedId if file doesn't exist", async () => {
|
|
111
|
+
const result = await importComponents("/file.csv", {
|
|
112
|
+
csvColumnMapping: {
|
|
113
|
+
name: "name",
|
|
114
|
+
text: 2,
|
|
115
|
+
componentId: 0,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(result.componentsInserted).toBe(0);
|
|
120
|
+
expect(result.firstImportedId).toBe("null");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -24,6 +24,14 @@ export async function importComponents(
|
|
|
24
24
|
): Promise<ImportComponentResponse> {
|
|
25
25
|
const api = createApiClient();
|
|
26
26
|
|
|
27
|
+
if (!fs.existsSync(path)) {
|
|
28
|
+
console.error("Failed to import file: couldn't find file at path " + path);
|
|
29
|
+
return {
|
|
30
|
+
componentsInserted: 0,
|
|
31
|
+
firstImportedId: "null",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
const form = new FormData();
|
|
28
36
|
form.append("import", fs.createReadStream(path));
|
|
29
37
|
|
package/lib/init/project.test.ts
CHANGED
|
@@ -1,44 +1,21 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
|
|
4
2
|
const yaml = require("js-yaml");
|
|
5
|
-
const { createFileIfMissing } = require("../config");
|
|
6
3
|
|
|
7
4
|
import { _testing } from "./project";
|
|
8
5
|
|
|
9
|
-
const fakeProjectDir =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
__dirname,
|
|
13
|
-
"../../testing/fixtures/bad-yaml.yml"
|
|
14
|
-
);
|
|
15
|
-
const configMissingName = path.join(
|
|
16
|
-
__dirname,
|
|
17
|
-
"../../testing/fixtures/project-config-no-name.yml"
|
|
18
|
-
);
|
|
19
|
-
const configMissingId = path.join(
|
|
20
|
-
__dirname,
|
|
21
|
-
"../../testing/fixtures/project-config-no-id.yml"
|
|
22
|
-
);
|
|
23
|
-
const configLegit = path.join(
|
|
24
|
-
__dirname,
|
|
25
|
-
"../../testing/fixtures/project-config-working.yml"
|
|
26
|
-
);
|
|
6
|
+
const fakeProjectDir = "/";
|
|
7
|
+
|
|
8
|
+
jest.mock("fs");
|
|
27
9
|
|
|
28
10
|
describe("saveProject", () => {
|
|
29
|
-
const configFile =
|
|
11
|
+
const configFile = "/ditto/config.yml";
|
|
30
12
|
const projectName = "My Amazing Project";
|
|
31
13
|
const projectId = "5f284259ce1d451b2eb2e23c";
|
|
32
14
|
|
|
33
15
|
beforeEach(() => {
|
|
34
|
-
if (!fs.existsSync(fakeProjectDir)) fs.mkdirSync(fakeProjectDir);
|
|
35
16
|
_testing.saveProject(configFile, projectName, projectId);
|
|
36
17
|
});
|
|
37
18
|
|
|
38
|
-
afterEach(() => {
|
|
39
|
-
fs.rmdirSync(fakeProjectDir, { recursive: true });
|
|
40
|
-
});
|
|
41
|
-
|
|
42
19
|
it("creates a config file with config data", () => {
|
|
43
20
|
const fileContents = fs.readFileSync(configFile, "utf8");
|
|
44
21
|
const data = yaml.load(fileContents);
|
package/lib/init/token.test.ts
CHANGED
|
@@ -1,22 +1,51 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import fs from "fs";
|
|
3
2
|
import config from "../config";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
4
|
import { needsToken } from "./token";
|
|
5
|
+
import { _test } from "./token";
|
|
6
|
+
import { vol } from "memfs";
|
|
7
|
+
import { jest } from "@jest/globals";
|
|
8
|
+
import axios from "axios";
|
|
9
|
+
|
|
10
|
+
jest.mock("fs");
|
|
11
|
+
jest.mock("../api");
|
|
12
|
+
|
|
13
|
+
const axiosMocked = jest.mocked(axios);
|
|
14
|
+
|
|
15
|
+
const defaultEnv = { ...process.env };
|
|
5
16
|
|
|
6
|
-
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vol.reset();
|
|
19
|
+
process.env = { ...defaultEnv };
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("needsToken", () => {
|
|
7
23
|
it("is true if there is no config file", () => {
|
|
8
|
-
expect(needsToken(
|
|
24
|
+
expect(needsToken(randomUUID())).toBeTruthy();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("is false if there is a token in the environment", () => {
|
|
28
|
+
process.env.DITTO_API_KEY = "xxx-xxx-xxx";
|
|
29
|
+
expect(needsToken(randomUUID())).toBe(false);
|
|
9
30
|
});
|
|
10
31
|
|
|
11
32
|
describe("with a config file", () => {
|
|
12
33
|
let configFile = "";
|
|
13
34
|
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
configFile =
|
|
35
|
+
beforeEach(async () => {
|
|
36
|
+
configFile = `/${randomUUID()}`;
|
|
37
|
+
await new Promise((resolve, reject) =>
|
|
38
|
+
fs.writeFile(configFile, "", (err) => {
|
|
39
|
+
if (err) reject(err);
|
|
40
|
+
else {
|
|
41
|
+
resolve(null);
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
);
|
|
16
45
|
});
|
|
17
46
|
|
|
18
47
|
it("returns true if empty", () => {
|
|
19
|
-
expect(needsToken(configFile, "testing.
|
|
48
|
+
expect(needsToken(configFile, "testing.dittowords.com")).toBeTruthy();
|
|
20
49
|
});
|
|
21
50
|
|
|
22
51
|
describe("with some data", () => {
|
|
@@ -49,3 +78,22 @@ describe("needsToken()", () => {
|
|
|
49
78
|
});
|
|
50
79
|
});
|
|
51
80
|
});
|
|
81
|
+
|
|
82
|
+
const { verifyTokenUsingTokenCheck } = _test;
|
|
83
|
+
describe("verifyTokenUsingTokenCheck", () => {
|
|
84
|
+
it("returns success: true for api success response", async () => {
|
|
85
|
+
axiosMocked.get.mockResolvedValueOnce({ status: 200 });
|
|
86
|
+
const result = await verifyTokenUsingTokenCheck("xxx-xxx");
|
|
87
|
+
expect(result.success).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
it("returns success: false for api unauthorized response", async () => {
|
|
90
|
+
axiosMocked.get.mockResolvedValueOnce({ status: 401 });
|
|
91
|
+
const result = await verifyTokenUsingTokenCheck("xxx-xxx");
|
|
92
|
+
expect(result.success).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
it("returns success: false for api invalid response", async () => {
|
|
95
|
+
axiosMocked.get.mockResolvedValueOnce("error");
|
|
96
|
+
const result = await verifyTokenUsingTokenCheck("xxx-xxx");
|
|
97
|
+
expect(result.success).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
});
|