@lightdash/cli 2.228.1 → 2.229.1
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/dist/.tsbuildinfo.build +1 -0
- package/package.json +4 -4
- package/dist/.tsbuildinfo +0 -1
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -72
- package/dist/dbt/context.test.d.ts +0 -2
- package/dist/dbt/context.test.d.ts.map +0 -1
- package/dist/dbt/context.test.js +0 -152
- package/dist/dbt/manifest.test.d.ts +0 -2
- package/dist/dbt/manifest.test.d.ts.map +0 -1
- package/dist/dbt/manifest.test.js +0 -170
- package/dist/dbt/models.test.d.ts +0 -2
- package/dist/dbt/models.test.d.ts.map +0 -1
- package/dist/dbt/models.test.js +0 -139
- package/dist/dbt/profiles.test.d.ts +0 -2
- package/dist/dbt/profiles.test.d.ts.map +0 -1
- package/dist/dbt/profiles.test.js +0 -50
- package/dist/dbt/targets/athena.test.d.ts +0 -2
- package/dist/dbt/targets/athena.test.d.ts.map +0 -1
- package/dist/dbt/targets/athena.test.js +0 -78
- package/dist/dbt/targets/duckdb.test.d.ts +0 -2
- package/dist/dbt/targets/duckdb.test.d.ts.map +0 -1
- package/dist/dbt/targets/duckdb.test.js +0 -149
- package/dist/dbt/targets/redshift.test.d.ts +0 -2
- package/dist/dbt/targets/redshift.test.d.ts.map +0 -1
- package/dist/dbt/targets/redshift.test.js +0 -198
- package/dist/dbt/templating.test.d.ts +0 -2
- package/dist/dbt/templating.test.d.ts.map +0 -1
- package/dist/dbt/templating.test.js +0 -51
- package/dist/dbt/validation.test.d.ts +0 -2
- package/dist/dbt/validation.test.d.ts.map +0 -1
- package/dist/dbt/validation.test.js +0 -67
- package/dist/handlers/apps/appCodeFiles.test.d.ts +0 -2
- package/dist/handlers/apps/appCodeFiles.test.d.ts.map +0 -1
- package/dist/handlers/apps/appCodeFiles.test.js +0 -391
- package/dist/handlers/apps/appsDownload.test.d.ts +0 -2
- package/dist/handlers/apps/appsDownload.test.d.ts.map +0 -1
- package/dist/handlers/apps/appsDownload.test.js +0 -639
- package/dist/handlers/apps/build.test.d.ts +0 -2
- package/dist/handlers/apps/build.test.d.ts.map +0 -1
- package/dist/handlers/apps/build.test.js +0 -252
- package/dist/handlers/apps/createApp.test.d.ts +0 -2
- package/dist/handlers/apps/createApp.test.d.ts.map +0 -1
- package/dist/handlers/apps/createApp.test.js +0 -340
- package/dist/handlers/apps/pathContainment.test.d.ts +0 -2
- package/dist/handlers/apps/pathContainment.test.d.ts.map +0 -1
- package/dist/handlers/apps/pathContainment.test.js +0 -66
- package/dist/handlers/apps/preview.test.d.ts +0 -2
- package/dist/handlers/apps/preview.test.d.ts.map +0 -1
- package/dist/handlers/apps/preview.test.js +0 -353
- package/dist/handlers/apps/previewProxy.test.d.ts +0 -2
- package/dist/handlers/apps/previewProxy.test.d.ts.map +0 -1
- package/dist/handlers/apps/previewProxy.test.js +0 -178
- package/dist/handlers/apps/scaffolding.test.d.ts +0 -2
- package/dist/handlers/apps/scaffolding.test.d.ts.map +0 -1
- package/dist/handlers/apps/scaffolding.test.js +0 -151
- package/dist/handlers/apps/uploadRetry.test.d.ts +0 -2
- package/dist/handlers/apps/uploadRetry.test.d.ts.map +0 -1
- package/dist/handlers/apps/uploadRetry.test.js +0 -103
- package/dist/handlers/apps/validate.test.d.ts +0 -2
- package/dist/handlers/apps/validate.test.d.ts.map +0 -1
- package/dist/handlers/apps/validate.test.js +0 -526
- package/dist/handlers/compile.test.d.ts +0 -2
- package/dist/handlers/compile.test.d.ts.map +0 -1
- package/dist/handlers/compile.test.js +0 -151
- package/dist/handlers/compileProject.test.d.ts +0 -2
- package/dist/handlers/compileProject.test.d.ts.map +0 -1
- package/dist/handlers/compileProject.test.js +0 -637
- package/dist/handlers/compileWarehouseColumnValidation.test.d.ts +0 -2
- package/dist/handlers/compileWarehouseColumnValidation.test.d.ts.map +0 -1
- package/dist/handlers/compileWarehouseColumnValidation.test.js +0 -493
- package/dist/handlers/connectSnowflake.test.d.ts +0 -2
- package/dist/handlers/connectSnowflake.test.d.ts.map +0 -1
- package/dist/handlers/connectSnowflake.test.js +0 -320
- package/dist/handlers/contentAsCode/fileNames.test.d.ts +0 -2
- package/dist/handlers/contentAsCode/fileNames.test.d.ts.map +0 -1
- package/dist/handlers/contentAsCode/fileNames.test.js +0 -79
- package/dist/handlers/contentAsCode/registry.test.d.ts +0 -2
- package/dist/handlers/contentAsCode/registry.test.d.ts.map +0 -1
- package/dist/handlers/contentAsCode/registry.test.js +0 -18
- package/dist/handlers/contentAsCode/resource.test.d.ts +0 -2
- package/dist/handlers/contentAsCode/resource.test.d.ts.map +0 -1
- package/dist/handlers/contentAsCode/resource.test.js +0 -121
- package/dist/handlers/dbt/getDbtVersion.test.d.ts +0 -2
- package/dist/handlers/dbt/getDbtVersion.test.d.ts.map +0 -1
- package/dist/handlers/dbt/getDbtVersion.test.js +0 -106
- package/dist/handlers/deploy.test.d.ts +0 -2
- package/dist/handlers/deploy.test.d.ts.map +0 -1
- package/dist/handlers/deploy.test.js +0 -244
- package/dist/handlers/download.test.d.ts +0 -2
- package/dist/handlers/download.test.d.ts.map +0 -1
- package/dist/handlers/download.test.js +0 -1739
- package/dist/handlers/exportChartImage.test.d.ts +0 -2
- package/dist/handlers/exportChartImage.test.d.ts.map +0 -1
- package/dist/handlers/exportChartImage.test.js +0 -55
- package/dist/handlers/homepagesAsCode.test.d.ts +0 -2
- package/dist/handlers/homepagesAsCode.test.d.ts.map +0 -1
- package/dist/handlers/homepagesAsCode.test.js +0 -72
- package/dist/handlers/lint.test.d.ts +0 -2
- package/dist/handlers/lint.test.d.ts.map +0 -1
- package/dist/handlers/lint.test.js +0 -221
- package/dist/handlers/login.test.d.ts +0 -2
- package/dist/handlers/login.test.d.ts.map +0 -1
- package/dist/handlers/login.test.js +0 -24
- package/dist/handlers/metadataFile.test.d.ts +0 -2
- package/dist/handlers/metadataFile.test.d.ts.map +0 -1
- package/dist/handlers/metadataFile.test.js +0 -46
- package/dist/handlers/organizationContent/customRoles.test.d.ts +0 -2
- package/dist/handlers/organizationContent/customRoles.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/customRoles.test.js +0 -179
- package/dist/handlers/organizationContent/groups.test.d.ts +0 -2
- package/dist/handlers/organizationContent/groups.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/groups.test.js +0 -135
- package/dist/handlers/organizationContent/index.test.d.ts +0 -2
- package/dist/handlers/organizationContent/index.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/index.test.js +0 -212
- package/dist/handlers/organizationContent/themes.test.d.ts +0 -2
- package/dist/handlers/organizationContent/themes.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/themes.test.js +0 -180
- package/dist/handlers/organizationContent/users.test.d.ts +0 -2
- package/dist/handlers/organizationContent/users.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/users.test.js +0 -155
- package/dist/handlers/preAggregateAudit.test.d.ts +0 -2
- package/dist/handlers/preAggregateAudit.test.d.ts.map +0 -1
- package/dist/handlers/preAggregateAudit.test.js +0 -102
- package/dist/handlers/renameHandler.test.d.ts +0 -2
- package/dist/handlers/renameHandler.test.d.ts.map +0 -1
- package/dist/handlers/renameHandler.test.js +0 -189
- package/dist/handlers/resolveProjectFlag.test.d.ts +0 -2
- package/dist/handlers/resolveProjectFlag.test.d.ts.map +0 -1
- package/dist/handlers/resolveProjectFlag.test.js +0 -48
- package/dist/handlers/selectProject.test.d.ts +0 -2
- package/dist/handlers/selectProject.test.d.ts.map +0 -1
- package/dist/handlers/selectProject.test.js +0 -224
- package/dist/handlers/setProject.test.d.ts +0 -2
- package/dist/handlers/setProject.test.d.ts.map +0 -1
- package/dist/handlers/setProject.test.js +0 -63
- package/dist/handlers/slugUpdate.test.d.ts +0 -2
- package/dist/handlers/slugUpdate.test.d.ts.map +0 -1
- package/dist/handlers/slugUpdate.test.js +0 -265
- package/dist/handlers/timestampConversion.test.d.ts +0 -2
- package/dist/handlers/timestampConversion.test.d.ts.map +0 -1
- package/dist/handlers/timestampConversion.test.js +0 -87
- package/dist/handlers/upgradeCheck.test.d.ts +0 -2
- package/dist/handlers/upgradeCheck.test.d.ts.map +0 -1
- package/dist/handlers/upgradeCheck.test.js +0 -227
- package/dist/handlers/validate.test.d.ts +0 -2
- package/dist/handlers/validate.test.d.ts.map +0 -1
- package/dist/handlers/validate.test.js +0 -318
- package/dist/handlers/validateSpaceFilter.test.d.ts +0 -2
- package/dist/handlers/validateSpaceFilter.test.d.ts.map +0 -1
- package/dist/handlers/validateSpaceFilter.test.js +0 -79
- package/dist/handlers/warehouseConnection.test.d.ts +0 -2
- package/dist/handlers/warehouseConnection.test.d.ts.map +0 -1
- package/dist/handlers/warehouseConnection.test.js +0 -75
- package/dist/lightdash-config/lightdash-config.test.d.ts +0 -2
- package/dist/lightdash-config/lightdash-config.test.d.ts.map +0 -1
- package/dist/lightdash-config/lightdash-config.test.js +0 -69
- package/dist/prompts/fuzzySearchPrompt.test.d.ts +0 -2
- package/dist/prompts/fuzzySearchPrompt.test.d.ts.map +0 -1
- package/dist/prompts/fuzzySearchPrompt.test.js +0 -53
- package/dist/refsArgument.test.d.ts +0 -2
- package/dist/refsArgument.test.d.ts.map +0 -1
- package/dist/refsArgument.test.js +0 -42
- package/dist/releaseSafety/index.test.d.ts +0 -2
- package/dist/releaseSafety/index.test.d.ts.map +0 -1
- package/dist/releaseSafety/index.test.js +0 -154
- package/dist/releaseSafety/version.test.d.ts +0 -2
- package/dist/releaseSafety/version.test.d.ts.map +0 -1
- package/dist/releaseSafety/version.test.js +0 -22
- package/dist/skills.test.d.ts +0 -2
- package/dist/skills.test.d.ts.map +0 -1
- package/dist/skills.test.js +0 -112
- package/dist/terminal/contentAsCodeOutput.test.d.ts +0 -2
- package/dist/terminal/contentAsCodeOutput.test.d.ts.map +0 -1
- package/dist/terminal/contentAsCodeOutput.test.js +0 -231
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const common_1 = require("@lightdash/common");
|
|
5
|
-
const crypto_1 = require("crypto");
|
|
6
|
-
const fs_1 = require("fs");
|
|
7
|
-
const yaml = tslib_1.__importStar(require("js-yaml"));
|
|
8
|
-
const os = tslib_1.__importStar(require("os"));
|
|
9
|
-
const path = tslib_1.__importStar(require("path"));
|
|
10
|
-
const vitest_1 = require("vitest");
|
|
11
|
-
const apiClient_1 = require("../dbt/apiClient");
|
|
12
|
-
const groups_1 = require("./groups");
|
|
13
|
-
vitest_1.vi.mock('../dbt/apiClient', async (importOriginal) => ({
|
|
14
|
-
...(await importOriginal()),
|
|
15
|
-
lightdashApi: vitest_1.vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
describe('groups as code', () => {
|
|
18
|
-
let tmpDir;
|
|
19
|
-
const group = (name, members = []) => ({
|
|
20
|
-
version: 1,
|
|
21
|
-
name,
|
|
22
|
-
members,
|
|
23
|
-
});
|
|
24
|
-
const writeGroup = async (filename, value) => {
|
|
25
|
-
const folder = (0, groups_1.getGroupsFolder)(tmpDir);
|
|
26
|
-
await fs_1.promises.mkdir(folder, { recursive: true });
|
|
27
|
-
await fs_1.promises.writeFile(path.join(folder, filename), typeof value === 'string'
|
|
28
|
-
? value
|
|
29
|
-
: yaml.dump(value, { sortKeys: true }));
|
|
30
|
-
};
|
|
31
|
-
beforeEach(async () => {
|
|
32
|
-
vitest_1.vi.mocked(apiClient_1.lightdashApi).mockReset();
|
|
33
|
-
tmpDir = await fs_1.promises.mkdtemp(path.join(os.tmpdir(), 'groups-test-'));
|
|
34
|
-
});
|
|
35
|
-
afterEach(async () => {
|
|
36
|
-
vitest_1.vi.restoreAllMocks();
|
|
37
|
-
await fs_1.promises.rm(tmpDir, { recursive: true, force: true });
|
|
38
|
-
});
|
|
39
|
-
it('downloads deterministic groups, empty groups, and collision-safe filenames', async () => {
|
|
40
|
-
const folder = (0, groups_1.getGroupsFolder)(tmpDir);
|
|
41
|
-
await fs_1.promises.mkdir(folder, { recursive: true });
|
|
42
|
-
await fs_1.promises.writeFile(path.join(folder, 'stale.yml'), 'stale: true');
|
|
43
|
-
vitest_1.vi.mocked(apiClient_1.lightdashApi).mockResolvedValueOnce({
|
|
44
|
-
groups: [
|
|
45
|
-
group('Finance?', ['z@example.com', 'a@example.com']),
|
|
46
|
-
group('Empty'),
|
|
47
|
-
group('Finance!'),
|
|
48
|
-
],
|
|
49
|
-
});
|
|
50
|
-
await expect((0, groups_1.downloadGroups)('organization-uuid', tmpDir)).resolves.toBe(3);
|
|
51
|
-
const financeBangHash = (0, crypto_1.createHash)('sha256')
|
|
52
|
-
.update('Finance!')
|
|
53
|
-
.digest('hex')
|
|
54
|
-
.slice(0, 8);
|
|
55
|
-
const financeQuestionHash = (0, crypto_1.createHash)('sha256')
|
|
56
|
-
.update('Finance?')
|
|
57
|
-
.digest('hex')
|
|
58
|
-
.slice(0, 8);
|
|
59
|
-
expect((await fs_1.promises.readdir(folder)).sort()).toStrictEqual([
|
|
60
|
-
'empty.yml',
|
|
61
|
-
`finance-${financeBangHash}.yml`,
|
|
62
|
-
`finance-${financeQuestionHash}.yml`,
|
|
63
|
-
'stale.yml',
|
|
64
|
-
].sort());
|
|
65
|
-
expect(yaml.load(await fs_1.promises.readFile(path.join(folder, `finance-${financeQuestionHash}.yml`), 'utf8'))).toStrictEqual({
|
|
66
|
-
version: 1,
|
|
67
|
-
name: 'Finance?',
|
|
68
|
-
members: ['a@example.com', 'z@example.com'],
|
|
69
|
-
});
|
|
70
|
-
expect(yaml.load(await fs_1.promises.readFile(path.join(folder, 'empty.yml'), 'utf8'))).toStrictEqual({ version: 1, name: 'Empty', members: [] });
|
|
71
|
-
});
|
|
72
|
-
it('rejects duplicate exact names but preserves case-sensitive identity', async () => {
|
|
73
|
-
await writeGroup('one.yml', group('Finance'));
|
|
74
|
-
await writeGroup('two.yml', group('Finance'));
|
|
75
|
-
await expect((0, groups_1.readGroupFiles)(tmpDir)).rejects.toThrow('Duplicate group name "Finance"');
|
|
76
|
-
await fs_1.promises.rm((0, groups_1.getGroupsFolder)(tmpDir), { recursive: true });
|
|
77
|
-
await writeGroup('upper.yml', group('Finance'));
|
|
78
|
-
await writeGroup('lower.yml', group('finance'));
|
|
79
|
-
await expect((0, groups_1.readGroupFiles)(tmpDir)).resolves.toHaveLength(2);
|
|
80
|
-
});
|
|
81
|
-
it('is a backward-compatible no-op when the groups folder is missing', async () => {
|
|
82
|
-
await expect((0, groups_1.uploadGroups)('organization-uuid', tmpDir)).resolves.toMatchObject({
|
|
83
|
-
created: 0,
|
|
84
|
-
updated: 0,
|
|
85
|
-
unchanged: 0,
|
|
86
|
-
failed: 0,
|
|
87
|
-
dependencySkipped: 0,
|
|
88
|
-
});
|
|
89
|
-
expect(apiClient_1.lightdashApi).not.toHaveBeenCalled();
|
|
90
|
-
});
|
|
91
|
-
it('continues after malformed and API-failing group files', async () => {
|
|
92
|
-
await writeGroup('bad-yaml.yml', 'name: [unterminated');
|
|
93
|
-
await writeGroup('api-error.yml', group('API error'));
|
|
94
|
-
await writeGroup('valid.yml', group('Valid'));
|
|
95
|
-
vitest_1.vi.mocked(apiClient_1.lightdashApi)
|
|
96
|
-
.mockRejectedValueOnce(new Error('unknown member'))
|
|
97
|
-
.mockResolvedValueOnce({ action: common_1.PromotionAction.CREATE });
|
|
98
|
-
await expect((0, groups_1.uploadGroups)('organization-uuid', tmpDir)).resolves.toMatchObject({
|
|
99
|
-
created: 1,
|
|
100
|
-
updated: 0,
|
|
101
|
-
unchanged: 0,
|
|
102
|
-
failed: 2,
|
|
103
|
-
});
|
|
104
|
-
expect(apiClient_1.lightdashApi).toHaveBeenCalledTimes(2);
|
|
105
|
-
});
|
|
106
|
-
it('reports all promotion actions and sends the portable body', async () => {
|
|
107
|
-
await writeGroup('create.yml', group('Create'));
|
|
108
|
-
await writeGroup('same.yml', group('Same'));
|
|
109
|
-
await writeGroup('update.yml', group('Update', ['member@example.com']));
|
|
110
|
-
vitest_1.vi.mocked(apiClient_1.lightdashApi)
|
|
111
|
-
.mockResolvedValueOnce({ action: common_1.PromotionAction.CREATE })
|
|
112
|
-
.mockResolvedValueOnce({
|
|
113
|
-
action: common_1.PromotionAction.NO_CHANGES,
|
|
114
|
-
})
|
|
115
|
-
.mockResolvedValueOnce({ action: common_1.PromotionAction.UPDATE });
|
|
116
|
-
await expect((0, groups_1.uploadGroups)('organization-uuid', tmpDir)).resolves.toMatchObject({
|
|
117
|
-
created: 1,
|
|
118
|
-
updated: 1,
|
|
119
|
-
unchanged: 1,
|
|
120
|
-
failed: 0,
|
|
121
|
-
});
|
|
122
|
-
expect(apiClient_1.lightdashApi).toHaveBeenNthCalledWith(3, {
|
|
123
|
-
method: 'POST',
|
|
124
|
-
url: '/api/v2/orgs/organization-uuid/code/groups',
|
|
125
|
-
body: expect.any(String),
|
|
126
|
-
});
|
|
127
|
-
expect(JSON.parse(vitest_1.vi.mocked(apiClient_1.lightdashApi).mock.calls[2][0].body)).toStrictEqual(group('Update', ['member@example.com']));
|
|
128
|
-
});
|
|
129
|
-
it('counts files skipped because a dependency phase failed', async () => {
|
|
130
|
-
await writeGroup('one.yml', group('One'));
|
|
131
|
-
await writeGroup('two.yml', 'invalid: [yaml');
|
|
132
|
-
await fs_1.promises.writeFile(path.join((0, groups_1.getGroupsFolder)(tmpDir), 'ignored.yaml'), 'name: ignored');
|
|
133
|
-
await expect((0, groups_1.countDependencySkippedGroups)(tmpDir)).resolves.toBe(2);
|
|
134
|
-
});
|
|
135
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/handlers/organizationContent/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const common_1 = require("@lightdash/common");
|
|
5
|
-
const analytics_1 = require("../../analytics/analytics");
|
|
6
|
-
const globalState_1 = tslib_1.__importDefault(require("../../globalState"));
|
|
7
|
-
const customRoles_1 = require("./customRoles");
|
|
8
|
-
const groups_1 = require("./groups");
|
|
9
|
-
const index_1 = require("./index");
|
|
10
|
-
const themes_1 = require("./themes");
|
|
11
|
-
const users_1 = require("./users");
|
|
12
|
-
vi.mock('./customRoles', async (importOriginal) => ({
|
|
13
|
-
...(await importOriginal()),
|
|
14
|
-
downloadCustomRoles: vi.fn(),
|
|
15
|
-
uploadCustomRoles: vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
vi.mock('./users', async (importOriginal) => ({
|
|
18
|
-
...(await importOriginal()),
|
|
19
|
-
downloadUsers: vi.fn(),
|
|
20
|
-
uploadUsers: vi.fn(),
|
|
21
|
-
}));
|
|
22
|
-
vi.mock('./groups', async (importOriginal) => ({
|
|
23
|
-
...(await importOriginal()),
|
|
24
|
-
downloadGroups: vi.fn(),
|
|
25
|
-
uploadGroups: vi.fn(),
|
|
26
|
-
countDependencySkippedGroups: vi.fn(),
|
|
27
|
-
}));
|
|
28
|
-
vi.mock('./themes', async (importOriginal) => ({
|
|
29
|
-
...(await importOriginal()),
|
|
30
|
-
downloadThemes: vi.fn(),
|
|
31
|
-
prepareThemeUploads: vi.fn(),
|
|
32
|
-
uploadThemes: vi.fn(),
|
|
33
|
-
}));
|
|
34
|
-
describe('organization content download', () => {
|
|
35
|
-
const spinner = {
|
|
36
|
-
start: vi.fn(),
|
|
37
|
-
succeed: vi.fn(),
|
|
38
|
-
warn: vi.fn(),
|
|
39
|
-
stop: vi.fn(),
|
|
40
|
-
fail: vi.fn(),
|
|
41
|
-
};
|
|
42
|
-
const config = {
|
|
43
|
-
user: {
|
|
44
|
-
userUuid: 'user-uuid',
|
|
45
|
-
organizationUuid: 'organization-uuid',
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
beforeEach(() => {
|
|
49
|
-
vi.clearAllMocks();
|
|
50
|
-
vi.spyOn(globalState_1.default, 'startSpinner').mockReturnValue(spinner);
|
|
51
|
-
vi.spyOn(globalState_1.default, 'log').mockImplementation(() => undefined);
|
|
52
|
-
vi.spyOn(globalState_1.default, 'debug').mockImplementation(() => undefined);
|
|
53
|
-
vi.spyOn(analytics_1.LightdashAnalytics, 'track').mockResolvedValue(undefined);
|
|
54
|
-
vi.mocked(customRoles_1.downloadCustomRoles).mockResolvedValue(2);
|
|
55
|
-
vi.mocked(users_1.downloadUsers).mockResolvedValue(3);
|
|
56
|
-
vi.mocked(groups_1.downloadGroups).mockResolvedValue(4);
|
|
57
|
-
vi.mocked(themes_1.downloadThemes).mockResolvedValue(5);
|
|
58
|
-
});
|
|
59
|
-
it('skips groups when the group service is disabled', async () => {
|
|
60
|
-
vi.mocked(groups_1.downloadGroups).mockRejectedValue(new common_1.LightdashError({
|
|
61
|
-
message: 'Group service is not enabled',
|
|
62
|
-
name: 'ForbiddenError',
|
|
63
|
-
statusCode: 403,
|
|
64
|
-
data: {},
|
|
65
|
-
}));
|
|
66
|
-
await expect((0, index_1.downloadOrganizationContent)({ config })).resolves.toBeUndefined();
|
|
67
|
-
expect(spinner.warn).toHaveBeenCalledOnce();
|
|
68
|
-
expect(spinner.fail).not.toHaveBeenCalled();
|
|
69
|
-
expect(globalState_1.default.debug).toHaveBeenCalledWith('> Warning: groups were not downloaded because the group service is not enabled');
|
|
70
|
-
});
|
|
71
|
-
it('reports a duration for every downloaded resource', async () => {
|
|
72
|
-
await (0, index_1.downloadOrganizationContent)({ config });
|
|
73
|
-
expect(spinner.succeed).toHaveBeenCalledTimes(4);
|
|
74
|
-
spinner.succeed.mock.calls.forEach(([message]) => expect(message).toMatch(/\(\d+ms\)$/));
|
|
75
|
-
});
|
|
76
|
-
it('still fails for other group download errors', async () => {
|
|
77
|
-
vi.mocked(groups_1.downloadGroups).mockRejectedValue(new Error('Network error'));
|
|
78
|
-
await expect((0, index_1.downloadOrganizationContent)({ config })).rejects.toThrow('Network error');
|
|
79
|
-
expect(spinner.fail).toHaveBeenCalledOnce();
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
describe('organization content upload sequencing', () => {
|
|
83
|
-
const spinner = {
|
|
84
|
-
start: vi.fn(),
|
|
85
|
-
succeed: vi.fn(),
|
|
86
|
-
warn: vi.fn(),
|
|
87
|
-
stop: vi.fn(),
|
|
88
|
-
fail: vi.fn(),
|
|
89
|
-
};
|
|
90
|
-
const config = {
|
|
91
|
-
user: {
|
|
92
|
-
userUuid: 'user-uuid',
|
|
93
|
-
organizationUuid: 'organization-uuid',
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
const customRoleSummary = (failed = 0) => ({
|
|
97
|
-
created: 0,
|
|
98
|
-
updated: 0,
|
|
99
|
-
unchanged: 0,
|
|
100
|
-
failed,
|
|
101
|
-
failures: failed > 0 ? [{ message: 'role failure' }] : [],
|
|
102
|
-
});
|
|
103
|
-
const userSummary = (failed = 0) => ({
|
|
104
|
-
created: 0,
|
|
105
|
-
updated: 0,
|
|
106
|
-
unchanged: 0,
|
|
107
|
-
awaitingAuthentication: 0,
|
|
108
|
-
invited: 0,
|
|
109
|
-
skippedAuthenticated: 0,
|
|
110
|
-
skippedDisabled: 0,
|
|
111
|
-
skippedValidInvite: 0,
|
|
112
|
-
failed,
|
|
113
|
-
failures: failed > 0 ? [{ message: 'user failure' }] : [],
|
|
114
|
-
});
|
|
115
|
-
const groupSummary = (failed = 0) => ({
|
|
116
|
-
created: 0,
|
|
117
|
-
updated: 0,
|
|
118
|
-
unchanged: 0,
|
|
119
|
-
failed,
|
|
120
|
-
dependencySkipped: 0,
|
|
121
|
-
failures: failed > 0 ? [{ message: 'group failure' }] : [],
|
|
122
|
-
});
|
|
123
|
-
const themeSummary = (failed = 0) => ({
|
|
124
|
-
created: failed > 0 ? 1 : 0,
|
|
125
|
-
updated: 0,
|
|
126
|
-
unchanged: 0,
|
|
127
|
-
failed,
|
|
128
|
-
completedSlugs: failed > 0 ? ['processed-theme'] : [],
|
|
129
|
-
failures: failed > 0 ? [{ message: 'theme failure' }] : [],
|
|
130
|
-
});
|
|
131
|
-
beforeEach(() => {
|
|
132
|
-
vi.clearAllMocks();
|
|
133
|
-
vi.spyOn(globalState_1.default, 'startSpinner').mockReturnValue(spinner);
|
|
134
|
-
vi.spyOn(globalState_1.default, 'log').mockImplementation(() => undefined);
|
|
135
|
-
vi.spyOn(analytics_1.LightdashAnalytics, 'track').mockResolvedValue(undefined);
|
|
136
|
-
vi.mocked(customRoles_1.uploadCustomRoles).mockResolvedValue(customRoleSummary());
|
|
137
|
-
vi.mocked(users_1.uploadUsers).mockResolvedValue(userSummary());
|
|
138
|
-
vi.mocked(groups_1.uploadGroups).mockResolvedValue(groupSummary());
|
|
139
|
-
vi.mocked(themes_1.prepareThemeUploads).mockResolvedValue([]);
|
|
140
|
-
vi.mocked(themes_1.uploadThemes).mockResolvedValue(themeSummary());
|
|
141
|
-
vi.mocked(groups_1.countDependencySkippedGroups).mockResolvedValue(2);
|
|
142
|
-
});
|
|
143
|
-
it('preflights themes, then awaits organization resource uploads in order', async () => {
|
|
144
|
-
const phaseOrder = [];
|
|
145
|
-
vi.mocked(themes_1.prepareThemeUploads).mockImplementation(async () => {
|
|
146
|
-
phaseOrder.push('themes:preflight:start', 'themes:preflight:end');
|
|
147
|
-
return [];
|
|
148
|
-
});
|
|
149
|
-
vi.mocked(customRoles_1.uploadCustomRoles).mockImplementation(async () => {
|
|
150
|
-
phaseOrder.push('roles:start', 'roles:end');
|
|
151
|
-
return customRoleSummary();
|
|
152
|
-
});
|
|
153
|
-
vi.mocked(users_1.uploadUsers).mockImplementation(async () => {
|
|
154
|
-
phaseOrder.push('users:start', 'users:end');
|
|
155
|
-
return userSummary();
|
|
156
|
-
});
|
|
157
|
-
vi.mocked(groups_1.uploadGroups).mockImplementation(async () => {
|
|
158
|
-
phaseOrder.push('groups:start', 'groups:end');
|
|
159
|
-
return groupSummary();
|
|
160
|
-
});
|
|
161
|
-
vi.mocked(themes_1.uploadThemes).mockImplementation(async () => {
|
|
162
|
-
phaseOrder.push('themes:start', 'themes:end');
|
|
163
|
-
return themeSummary();
|
|
164
|
-
});
|
|
165
|
-
await (0, index_1.uploadOrganizationContent)({ config });
|
|
166
|
-
expect(phaseOrder).toStrictEqual([
|
|
167
|
-
'themes:preflight:start',
|
|
168
|
-
'themes:preflight:end',
|
|
169
|
-
'roles:start',
|
|
170
|
-
'roles:end',
|
|
171
|
-
'users:start',
|
|
172
|
-
'users:end',
|
|
173
|
-
'groups:start',
|
|
174
|
-
'groups:end',
|
|
175
|
-
'themes:start',
|
|
176
|
-
'themes:end',
|
|
177
|
-
]);
|
|
178
|
-
});
|
|
179
|
-
it('reports a duration for every uploaded resource', async () => {
|
|
180
|
-
await (0, index_1.uploadOrganizationContent)({ config });
|
|
181
|
-
expect(spinner.succeed).toHaveBeenCalledTimes(4);
|
|
182
|
-
spinner.succeed.mock.calls.forEach(([message]) => expect(message).toMatch(/\(\d+ms\)$/));
|
|
183
|
-
});
|
|
184
|
-
it('does not start users or groups when custom roles fail', async () => {
|
|
185
|
-
vi.mocked(customRoles_1.uploadCustomRoles).mockResolvedValue(customRoleSummary(1));
|
|
186
|
-
await expect((0, index_1.uploadOrganizationContent)({ config })).rejects.toThrow('Processed custom roles');
|
|
187
|
-
expect(users_1.uploadUsers).not.toHaveBeenCalled();
|
|
188
|
-
expect(groups_1.uploadGroups).not.toHaveBeenCalled();
|
|
189
|
-
expect(themes_1.uploadThemes).not.toHaveBeenCalled();
|
|
190
|
-
expect(groups_1.countDependencySkippedGroups).toHaveBeenCalledOnce();
|
|
191
|
-
});
|
|
192
|
-
it('does not start groups when users fail', async () => {
|
|
193
|
-
vi.mocked(users_1.uploadUsers).mockResolvedValue(userSummary(1));
|
|
194
|
-
await expect((0, index_1.uploadOrganizationContent)({ config })).rejects.toThrow('Processed users');
|
|
195
|
-
expect(groups_1.uploadGroups).not.toHaveBeenCalled();
|
|
196
|
-
expect(themes_1.uploadThemes).not.toHaveBeenCalled();
|
|
197
|
-
expect(groups_1.countDependencySkippedGroups).toHaveBeenCalledOnce();
|
|
198
|
-
});
|
|
199
|
-
it('does not mutate organization resources when theme preflight fails', async () => {
|
|
200
|
-
vi.mocked(themes_1.prepareThemeUploads).mockRejectedValue(new Error('Invalid theme'));
|
|
201
|
-
await expect((0, index_1.uploadOrganizationContent)({ config })).rejects.toThrow('Invalid theme');
|
|
202
|
-
expect(customRoles_1.uploadCustomRoles).not.toHaveBeenCalled();
|
|
203
|
-
expect(users_1.uploadUsers).not.toHaveBeenCalled();
|
|
204
|
-
expect(groups_1.uploadGroups).not.toHaveBeenCalled();
|
|
205
|
-
expect(themes_1.uploadThemes).not.toHaveBeenCalled();
|
|
206
|
-
});
|
|
207
|
-
it('fails after reporting a partial theme upload', async () => {
|
|
208
|
-
vi.mocked(themes_1.uploadThemes).mockResolvedValue(themeSummary(1));
|
|
209
|
-
await expect((0, index_1.uploadOrganizationContent)({ config })).rejects.toThrow('Processed themes: 1 created, 0 updated, 0 unchanged, 1 failed');
|
|
210
|
-
expect(globalState_1.default.log).toHaveBeenCalledWith(expect.stringContaining('processed-theme'));
|
|
211
|
-
});
|
|
212
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"themes.test.d.ts","sourceRoot":"","sources":["../../../src/handlers/organizationContent/themes.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const common_1 = require("@lightdash/common");
|
|
5
|
-
const fs_1 = require("fs");
|
|
6
|
-
const node_fetch_1 = require("node-fetch");
|
|
7
|
-
const os = tslib_1.__importStar(require("os"));
|
|
8
|
-
const path = tslib_1.__importStar(require("path"));
|
|
9
|
-
const yaml_1 = require("yaml");
|
|
10
|
-
const apiClient_1 = require("../dbt/apiClient");
|
|
11
|
-
const themes_1 = require("./themes");
|
|
12
|
-
vi.mock('../dbt/apiClient', () => ({
|
|
13
|
-
lightdashApi: vi.fn(),
|
|
14
|
-
lightdashRawApi: vi.fn(),
|
|
15
|
-
}));
|
|
16
|
-
const manifest = (slug) => ({
|
|
17
|
-
codeVersion: 1,
|
|
18
|
-
slug,
|
|
19
|
-
name: 'Acme Brand',
|
|
20
|
-
description: null,
|
|
21
|
-
extraInstructions: null,
|
|
22
|
-
});
|
|
23
|
-
const createThemeDirectory = async (contentRoot, slug) => {
|
|
24
|
-
const directory = path.join(contentRoot, 'themes', slug);
|
|
25
|
-
await Promise.all(['css', 'fonts', 'images', 'instructions'].map((folder) => fs_1.promises.mkdir(path.join(directory, folder), { recursive: true })));
|
|
26
|
-
await fs_1.promises.writeFile(path.join(directory, 'lightdash-theme.yml'), (0, yaml_1.stringify)(manifest(slug)));
|
|
27
|
-
await fs_1.promises.writeFile(path.join(directory, 'css', 'theme.css'), ':root { --brand: #123456; }');
|
|
28
|
-
return directory;
|
|
29
|
-
};
|
|
30
|
-
describe('organization theme content-as-code', () => {
|
|
31
|
-
let temporaryRoot;
|
|
32
|
-
beforeEach(async () => {
|
|
33
|
-
vi.clearAllMocks();
|
|
34
|
-
vi.mocked(apiClient_1.lightdashApi).mockResolvedValue([]);
|
|
35
|
-
temporaryRoot = await fs_1.promises.mkdtemp(path.join(os.tmpdir(), 'themes-test-'));
|
|
36
|
-
});
|
|
37
|
-
afterEach(async () => {
|
|
38
|
-
await fs_1.promises.rm(temporaryRoot, { recursive: true, force: true });
|
|
39
|
-
});
|
|
40
|
-
it('treats a missing themes directory as an empty upload', async () => {
|
|
41
|
-
await expect((0, themes_1.prepareThemeUploads)(temporaryRoot)).resolves.toEqual([]);
|
|
42
|
-
});
|
|
43
|
-
it('rejects nested paths during preflight', async () => {
|
|
44
|
-
const themeDirectory = await createThemeDirectory(temporaryRoot, 'acme-brand');
|
|
45
|
-
await fs_1.promises.mkdir(path.join(themeDirectory, 'css', 'nested'));
|
|
46
|
-
await expect((0, themes_1.prepareThemeUploads)(temporaryRoot)).rejects.toThrow('without symlinks or nested directories');
|
|
47
|
-
});
|
|
48
|
-
it('rejects symlinked files during preflight', async () => {
|
|
49
|
-
const themeDirectory = await createThemeDirectory(temporaryRoot, 'acme-brand');
|
|
50
|
-
await fs_1.promises.symlink(path.join(themeDirectory, 'css', 'theme.css'), path.join(themeDirectory, 'css', 'linked.css'));
|
|
51
|
-
await expect((0, themes_1.prepareThemeUploads)(temporaryRoot)).rejects.toThrow('without symlinks or nested directories');
|
|
52
|
-
});
|
|
53
|
-
it('accepts Git-dropped empty directories and ignores local filesystem artifacts', async () => {
|
|
54
|
-
const themeDirectory = await createThemeDirectory(temporaryRoot, 'acme-brand');
|
|
55
|
-
await Promise.all(['fonts', 'images', 'instructions'].map((folder) => fs_1.promises.rmdir(path.join(themeDirectory, folder))));
|
|
56
|
-
await Promise.all([
|
|
57
|
-
fs_1.promises.writeFile(path.join(temporaryRoot, 'themes', '.DS_Store'), 'metadata'),
|
|
58
|
-
fs_1.promises.writeFile(path.join(themeDirectory, '.DS_Store'), 'metadata'),
|
|
59
|
-
fs_1.promises.writeFile(path.join(themeDirectory, 'css', '.DS_Store'), 'metadata'),
|
|
60
|
-
fs_1.promises.mkdir(path.join(temporaryRoot, 'themes', '.lightdash-theme-ABC123')),
|
|
61
|
-
fs_1.promises.mkdir(path.join(temporaryRoot, 'themes', 'acme-brand.backup-00000000-0000-4000-8000-000000000001')),
|
|
62
|
-
]);
|
|
63
|
-
const prepared = await (0, themes_1.prepareThemeUploads)(temporaryRoot);
|
|
64
|
-
expect(prepared).toHaveLength(1);
|
|
65
|
-
expect(prepared[0].manifest.slug).toBe('acme-brand');
|
|
66
|
-
});
|
|
67
|
-
it('round-trips a downloaded package and replaces stale theme files', async () => {
|
|
68
|
-
const sourceRoot = path.join(temporaryRoot, 'source');
|
|
69
|
-
const destinationRoot = path.join(temporaryRoot, 'destination');
|
|
70
|
-
await createThemeDirectory(sourceRoot, 'acme-brand');
|
|
71
|
-
const [prepared] = await (0, themes_1.prepareThemeUploads)(sourceRoot);
|
|
72
|
-
const staleDirectory = await createThemeDirectory(destinationRoot, 'acme-brand');
|
|
73
|
-
await createThemeDirectory(destinationRoot, 'remotely-deleted');
|
|
74
|
-
await fs_1.promises.writeFile(path.join(staleDirectory, 'stale.txt'), 'stale');
|
|
75
|
-
vi.mocked(apiClient_1.lightdashApi).mockResolvedValue([
|
|
76
|
-
{ slug: 'acme-brand' },
|
|
77
|
-
]);
|
|
78
|
-
vi.mocked(apiClient_1.lightdashRawApi).mockResolvedValue(new node_fetch_1.Response(prepared.archive));
|
|
79
|
-
await expect((0, themes_1.downloadThemes)(destinationRoot)).resolves.toBe(1);
|
|
80
|
-
await expect(fs_1.promises.readFile(path.join(destinationRoot, 'themes', 'acme-brand', 'css', 'theme.css'), 'utf8')).resolves.toContain('--brand');
|
|
81
|
-
await expect(fs_1.promises.stat(path.join(destinationRoot, 'themes', 'acme-brand', 'stale.txt'))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
82
|
-
await expect(fs_1.promises.stat(path.join(destinationRoot, 'themes', 'remotely-deleted'))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
83
|
-
const [downloaded] = await (0, themes_1.prepareThemeUploads)(destinationRoot);
|
|
84
|
-
expect(downloaded.archive).toEqual(prepared.archive);
|
|
85
|
-
});
|
|
86
|
-
it('keeps the previous local theme set when a package download fails', async () => {
|
|
87
|
-
const sourceRoot = path.join(temporaryRoot, 'source');
|
|
88
|
-
const destinationRoot = path.join(temporaryRoot, 'destination');
|
|
89
|
-
await createThemeDirectory(sourceRoot, 'first-theme');
|
|
90
|
-
const [prepared] = await (0, themes_1.prepareThemeUploads)(sourceRoot);
|
|
91
|
-
const existingDirectory = await createThemeDirectory(destinationRoot, 'existing-theme');
|
|
92
|
-
await fs_1.promises.writeFile(path.join(existingDirectory, 'local-marker.txt'), 'keep me');
|
|
93
|
-
vi.mocked(apiClient_1.lightdashApi).mockResolvedValue([
|
|
94
|
-
{ slug: 'first-theme' },
|
|
95
|
-
{ slug: 'second-theme' },
|
|
96
|
-
]);
|
|
97
|
-
vi.mocked(apiClient_1.lightdashRawApi)
|
|
98
|
-
.mockResolvedValueOnce(new node_fetch_1.Response(prepared.archive))
|
|
99
|
-
.mockResolvedValueOnce(new node_fetch_1.Response(Buffer.from('not a tar')));
|
|
100
|
-
await expect((0, themes_1.downloadThemes)(destinationRoot)).rejects.toThrow();
|
|
101
|
-
await expect(fs_1.promises.readFile(path.join(existingDirectory, 'local-marker.txt'), 'utf8')).resolves.toBe('keep me');
|
|
102
|
-
await expect(fs_1.promises.stat(path.join(destinationRoot, 'themes', 'first-theme'))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
103
|
-
});
|
|
104
|
-
it('reports completed and failed imports without claiming full success', async () => {
|
|
105
|
-
await createThemeDirectory(temporaryRoot, 'first-theme');
|
|
106
|
-
await createThemeDirectory(temporaryRoot, 'second-theme');
|
|
107
|
-
const prepared = await (0, themes_1.prepareThemeUploads)(temporaryRoot);
|
|
108
|
-
vi.mocked(apiClient_1.lightdashRawApi)
|
|
109
|
-
.mockResolvedValueOnce(new node_fetch_1.Response(JSON.stringify({
|
|
110
|
-
status: 'ok',
|
|
111
|
-
results: {
|
|
112
|
-
slug: 'first-theme',
|
|
113
|
-
action: common_1.PromotionAction.CREATE,
|
|
114
|
-
},
|
|
115
|
-
})))
|
|
116
|
-
.mockRejectedValueOnce(new Error('remote failure'));
|
|
117
|
-
const summary = await (0, themes_1.uploadThemes)(prepared);
|
|
118
|
-
expect(summary).toEqual({
|
|
119
|
-
created: 1,
|
|
120
|
-
updated: 0,
|
|
121
|
-
unchanged: 0,
|
|
122
|
-
failed: 1,
|
|
123
|
-
completedSlugs: ['first-theme'],
|
|
124
|
-
failures: [
|
|
125
|
-
{
|
|
126
|
-
message: 'Failed to upload theme "second-theme": remote failure',
|
|
127
|
-
},
|
|
128
|
-
],
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
it('reports the action returned for every successful import', async () => {
|
|
132
|
-
await createThemeDirectory(temporaryRoot, 'created-theme');
|
|
133
|
-
await createThemeDirectory(temporaryRoot, 'updated-theme');
|
|
134
|
-
await createThemeDirectory(temporaryRoot, 'unchanged-theme');
|
|
135
|
-
const prepared = await (0, themes_1.prepareThemeUploads)(temporaryRoot);
|
|
136
|
-
for (const theme of prepared) {
|
|
137
|
-
const actionBySlug = {
|
|
138
|
-
'created-theme': common_1.PromotionAction.CREATE,
|
|
139
|
-
'updated-theme': common_1.PromotionAction.UPDATE,
|
|
140
|
-
'unchanged-theme': common_1.PromotionAction.NO_CHANGES,
|
|
141
|
-
};
|
|
142
|
-
vi.mocked(apiClient_1.lightdashRawApi).mockResolvedValueOnce(new node_fetch_1.Response(JSON.stringify({
|
|
143
|
-
status: 'ok',
|
|
144
|
-
results: {
|
|
145
|
-
slug: theme.manifest.slug,
|
|
146
|
-
action: actionBySlug[theme.manifest.slug],
|
|
147
|
-
},
|
|
148
|
-
})));
|
|
149
|
-
}
|
|
150
|
-
await expect((0, themes_1.uploadThemes)(prepared)).resolves.toMatchObject({
|
|
151
|
-
created: 1,
|
|
152
|
-
updated: 1,
|
|
153
|
-
unchanged: 1,
|
|
154
|
-
failed: 0,
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
it('classifies successful imports returned by older servers', async () => {
|
|
158
|
-
await createThemeDirectory(temporaryRoot, 'existing-theme');
|
|
159
|
-
await createThemeDirectory(temporaryRoot, 'new-theme');
|
|
160
|
-
const prepared = await (0, themes_1.prepareThemeUploads)(temporaryRoot);
|
|
161
|
-
vi.mocked(apiClient_1.lightdashApi).mockResolvedValue([
|
|
162
|
-
{ slug: 'existing-theme' },
|
|
163
|
-
]);
|
|
164
|
-
vi.mocked(apiClient_1.lightdashRawApi)
|
|
165
|
-
.mockResolvedValueOnce(new node_fetch_1.Response(JSON.stringify({
|
|
166
|
-
status: 'ok',
|
|
167
|
-
results: { slug: 'existing-theme' },
|
|
168
|
-
})))
|
|
169
|
-
.mockResolvedValueOnce(new node_fetch_1.Response(JSON.stringify({
|
|
170
|
-
status: 'ok',
|
|
171
|
-
results: { slug: 'new-theme' },
|
|
172
|
-
})));
|
|
173
|
-
await expect((0, themes_1.uploadThemes)(prepared)).resolves.toMatchObject({
|
|
174
|
-
created: 1,
|
|
175
|
-
updated: 1,
|
|
176
|
-
unchanged: 0,
|
|
177
|
-
failed: 0,
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"users.test.d.ts","sourceRoot":"","sources":["../../../src/handlers/organizationContent/users.test.ts"],"names":[],"mappings":""}
|