@usebruno/filestore 0.10.0 → 0.12.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/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/workers/worker-script.js +1 -1
- package/dist/cjs/workers/worker-script.js.map +1 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/workers/worker-script.js +1 -1
- package/dist/esm/workers/worker-script.js.map +1 -1
- package/dist/formats/bru/index.d.ts.map +1 -1
- package/dist/formats/bru/utils/redact-large-text-blocks.d.ts +11 -0
- package/dist/formats/bru/utils/redact-large-text-blocks.d.ts.map +1 -0
- package/dist/formats/yml/common/app.d.ts +8 -0
- package/dist/formats/yml/common/app.d.ts.map +1 -0
- package/dist/formats/yml/common/auth.d.ts.map +1 -1
- package/dist/formats/yml/common/body.d.ts.map +1 -1
- package/dist/formats/yml/common/datatype.d.ts +14 -0
- package/dist/formats/yml/common/datatype.d.ts.map +1 -0
- package/dist/formats/yml/common/headers.d.ts.map +1 -1
- package/dist/formats/yml/common/variables.d.ts +1 -2
- package/dist/formats/yml/common/variables.d.ts.map +1 -1
- package/dist/formats/yml/index.d.ts +3 -1
- package/dist/formats/yml/index.d.ts.map +1 -1
- package/dist/formats/yml/items/parseApp.d.ts +13 -0
- package/dist/formats/yml/items/parseApp.d.ts.map +1 -0
- package/dist/formats/yml/items/parseGraphQLRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/parseGrpcRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/parseHttpRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/parseWebsocketRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyApp.d.ts +4 -0
- package/dist/formats/yml/items/stringifyApp.d.ts.map +1 -0
- package/dist/formats/yml/items/stringifyGraphQLRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyGrpcRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyHttpRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyWebsocketRequest.d.ts.map +1 -1
- package/dist/formats/yml/mockServerTypes.d.ts +95 -0
- package/dist/formats/yml/mockServerTypes.d.ts.map +1 -0
- package/dist/formats/yml/parseCollection.d.ts.map +1 -1
- package/dist/formats/yml/parseEnvironment.d.ts +3 -1
- package/dist/formats/yml/parseEnvironment.d.ts.map +1 -1
- package/dist/formats/yml/parseFolder.d.ts.map +1 -1
- package/dist/formats/yml/parseItem.d.ts.map +1 -1
- package/dist/formats/yml/parseMockServer.d.ts +4 -0
- package/dist/formats/yml/parseMockServer.d.ts.map +1 -0
- package/dist/formats/yml/stringifyCollection.d.ts.map +1 -1
- package/dist/formats/yml/stringifyEnvironment.d.ts +3 -1
- package/dist/formats/yml/stringifyEnvironment.d.ts.map +1 -1
- package/dist/formats/yml/stringifyFolder.d.ts.map +1 -1
- package/dist/formats/yml/stringifyItem.d.ts.map +1 -1
- package/dist/formats/yml/stringifyMockServer.d.ts +4 -0
- package/dist/formats/yml/stringifyMockServer.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/secretsJsonToEnvironments.d.ts +38 -0
- package/dist/utils/secretsJsonToEnvironments.d.ts.map +1 -0
- package/dist/workers/WorkerQueue/index.d.ts +3 -2
- package/dist/workers/WorkerQueue/index.d.ts.map +1 -1
- package/dist/workers/index.d.ts +4 -0
- package/dist/workers/index.d.ts.map +1 -1
- package/package.json +7 -5
- package/src/constants.ts +13 -0
- package/src/formats/bru/index.ts +52 -0
- package/src/formats/bru/tests/parseBruEnvironment.spec.js +63 -0
- package/src/formats/bru/tests/redact-large-text-blocks.spec.js +472 -0
- package/src/formats/bru/utils/redact-large-text-blocks.ts +143 -0
- package/src/formats/yml/common/actions.ts +1 -1
- package/src/formats/yml/common/app.ts +27 -0
- package/src/formats/yml/common/auth.ts +53 -1
- package/src/formats/yml/common/body.spec.ts +62 -0
- package/src/formats/yml/common/body.ts +27 -9
- package/src/formats/yml/common/datatype.spec.ts +110 -0
- package/src/formats/yml/common/datatype.ts +57 -0
- package/src/formats/yml/common/headers.ts +4 -0
- package/src/formats/yml/common/variables.spec.ts +127 -0
- package/src/formats/yml/common/variables.ts +14 -22
- package/src/formats/yml/index.ts +5 -1
- package/src/formats/yml/items/parseApp.ts +37 -0
- package/src/formats/yml/items/parseGraphQLRequest.ts +14 -16
- package/src/formats/yml/items/parseGrpcRequest.ts +24 -3
- package/src/formats/yml/items/parseHttpRequest.ts +19 -16
- package/src/formats/yml/items/parseWebsocketRequest.ts +29 -9
- package/src/formats/yml/items/stringifyApp.ts +45 -0
- package/src/formats/yml/items/stringifyGraphQLRequest.ts +7 -6
- package/src/formats/yml/items/stringifyGrpcRequest.ts +9 -11
- package/src/formats/yml/items/stringifyHttpRequest.ts +14 -6
- package/src/formats/yml/items/stringifyWebsocketRequest.ts +22 -9
- package/src/formats/yml/mockServerTypes.ts +95 -0
- package/src/formats/yml/parseCollection.spec.ts +193 -0
- package/src/formats/yml/parseCollection.ts +24 -6
- package/src/formats/yml/parseEnvironment.spec.ts +99 -0
- package/src/formats/yml/parseEnvironment.ts +41 -19
- package/src/formats/yml/parseFolder.spec.ts +70 -0
- package/src/formats/yml/parseFolder.ts +19 -22
- package/src/formats/yml/parseItem.spec.ts +147 -0
- package/src/formats/yml/parseItem.ts +4 -0
- package/src/formats/yml/parseMockServer.spec.ts +180 -0
- package/src/formats/yml/parseMockServer.ts +113 -0
- package/src/formats/yml/stringifyCollection.spec.ts +223 -0
- package/src/formats/yml/stringifyCollection.ts +26 -9
- package/src/formats/yml/stringifyEnvironment.spec.ts +91 -0
- package/src/formats/yml/stringifyEnvironment.ts +55 -20
- package/src/formats/yml/stringifyFolder.spec.ts +70 -0
- package/src/formats/yml/stringifyFolder.ts +7 -2
- package/src/formats/yml/stringifyItem.spec.ts +53 -0
- package/src/formats/yml/stringifyItem.ts +4 -0
- package/src/formats/yml/stringifyMockServer.spec.ts +177 -0
- package/src/formats/yml/stringifyMockServer.ts +129 -0
- package/src/formats/yml/tests/defaultEnvironment.spec.js +50 -0
- package/src/formats/yml/tests/fixtures/presets/empty-request.yml +7 -0
- package/src/formats/yml/tests/fixtures/presets/no-presets.yml +3 -0
- package/src/formats/yml/tests/fixtures/presets/no-request-key.yml +6 -0
- package/src/formats/yml/tests/fixtures/presets/type-only-realistic.yml +24 -0
- package/src/formats/yml/tests/fixtures/presets/url-only-realistic.yml +25 -0
- package/src/formats/yml/tests/fixtures/presets/with-type-and-url.yml +9 -0
- package/src/formats/yml/tests/parseCollection.spec.js +63 -0
- package/src/formats/yml/tests/parseEnvironment.spec.js +24 -63
- package/src/formats/yml/tests/parseEnvironment.spec.ts +160 -0
- package/src/formats/yml/tests/stringifyEnvironment.spec.ts +230 -0
- package/src/formats/yml/tests/variables.spec.js +7 -7
- package/src/formats/yml/utils.ts +1 -1
- package/src/index.ts +39 -1
- package/src/types/bruno-lang.d.ts +1 -0
- package/src/types.ts +8 -0
- package/src/utils/secretsJsonToEnvironments.spec.ts +211 -0
- package/src/utils/secretsJsonToEnvironments.ts +129 -0
- package/src/workers/WorkerQueue/index.ts +10 -2
- package/src/workers/index.ts +25 -1
- package/src/workers/worker-script.ts +45 -21
- package/dist/formats/bru/tests/example-status-swap.spec.d.ts +0 -2
- package/dist/formats/bru/tests/example-status-swap.spec.d.ts.map +0 -1
- package/dist/formats/bru/tests/fixtures/oauth2-additional-params.d.ts +0 -57
- package/dist/formats/bru/tests/fixtures/oauth2-additional-params.d.ts.map +0 -1
- package/dist/formats/bru/tests/oauth2-additional-params.spec.d.ts +0 -2
- package/dist/formats/bru/tests/oauth2-additional-params.spec.d.ts.map +0 -1
- package/dist/formats/bru/tests/request-parse-and-redact-body-data.spec.d.ts +0 -2
- package/dist/formats/bru/tests/request-parse-and-redact-body-data.spec.d.ts.map +0 -1
- package/dist/formats/yml/tests/parseEnvironment.spec.d.ts +0 -2
- package/dist/formats/yml/tests/parseEnvironment.spec.d.ts.map +0 -1
- package/dist/formats/yml/tests/variables.spec.d.ts +0 -2
- package/dist/formats/yml/tests/variables.spec.d.ts.map +0 -1
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import stringifyCollection from './stringifyCollection';
|
|
2
|
+
import parseCollection from './parseCollection';
|
|
3
|
+
|
|
4
|
+
// Typed collection-root vars serialize to OC's `{ type, data }` struct.
|
|
5
|
+
// `dataType: 'string'` is the implicit default and stays a raw string.
|
|
6
|
+
|
|
7
|
+
describe('stringifyCollection — typed request.variables', () => {
|
|
8
|
+
it('round-trips typed values and omits a typed struct for the implicit string default', () => {
|
|
9
|
+
const collectionRoot = {
|
|
10
|
+
meta: null,
|
|
11
|
+
request: {
|
|
12
|
+
headers: [],
|
|
13
|
+
auth: { mode: 'none' },
|
|
14
|
+
script: { req: null, res: null },
|
|
15
|
+
tests: null,
|
|
16
|
+
vars: {
|
|
17
|
+
req: [
|
|
18
|
+
{ uid: 'v1', name: 'count', value: 42, enabled: true, dataType: 'number' },
|
|
19
|
+
{ uid: 'v2', name: 'enabled', value: true, enabled: true, dataType: 'boolean' },
|
|
20
|
+
{ uid: 'v3', name: 'config', value: { a: 1 }, enabled: true, dataType: 'object' },
|
|
21
|
+
{ uid: 'v4', name: 'greeting', value: 'hi', enabled: true, dataType: 'string' },
|
|
22
|
+
{ uid: 'v5', name: 'plain', value: 'hello', enabled: true }
|
|
23
|
+
],
|
|
24
|
+
res: []
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
docs: null
|
|
28
|
+
} as any;
|
|
29
|
+
|
|
30
|
+
const brunoConfig = { name: 'c' };
|
|
31
|
+
|
|
32
|
+
const yml = stringifyCollection(collectionRoot, brunoConfig);
|
|
33
|
+
|
|
34
|
+
// `type: string` is never written out.
|
|
35
|
+
expect(yml).not.toMatch(/type:\s*string/);
|
|
36
|
+
|
|
37
|
+
const { collectionRoot: reparsed } = parseCollection(yml);
|
|
38
|
+
const reqVars = reparsed.request!.vars!.req!;
|
|
39
|
+
|
|
40
|
+
expect(reqVars).toHaveLength(5);
|
|
41
|
+
expect(reqVars[0]).toMatchObject({ name: 'count', value: 42, dataType: 'number' });
|
|
42
|
+
expect(reqVars[1]).toMatchObject({ name: 'enabled', value: true, dataType: 'boolean' });
|
|
43
|
+
expect(reqVars[2]).toMatchObject({ name: 'config', value: { a: 1 }, dataType: 'object' });
|
|
44
|
+
expect(reqVars[3]).toMatchObject({ name: 'greeting', value: 'hi' });
|
|
45
|
+
expect(reqVars[3].dataType).toBeUndefined();
|
|
46
|
+
expect(reqVars[4]).toMatchObject({ name: 'plain', value: 'hello' });
|
|
47
|
+
expect(reqVars[4].dataType).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('stringifyCollection — script execution flow', () => {
|
|
52
|
+
const baseRoot = {
|
|
53
|
+
meta: null,
|
|
54
|
+
request: { headers: [], auth: { mode: 'none' }, script: { req: null, res: null }, tests: null, vars: { req: [], res: [] } },
|
|
55
|
+
docs: null
|
|
56
|
+
} as any;
|
|
57
|
+
|
|
58
|
+
it('round-trips flow: sequential through the bruno extension', () => {
|
|
59
|
+
const yml = stringifyCollection(baseRoot, { name: 'c', scripts: { flow: 'sequential' } });
|
|
60
|
+
expect(yml).toMatch(/flow:\s*sequential/);
|
|
61
|
+
expect(parseCollection(yml).brunoConfig.scripts?.flow).toBe('sequential');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('round-trips flow: sandwich through the bruno extension', () => {
|
|
65
|
+
const yml = stringifyCollection(baseRoot, { name: 'c', scripts: { flow: 'sandwich' } });
|
|
66
|
+
expect(parseCollection(yml).brunoConfig.scripts?.flow).toBe('sandwich');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('writes no flow when the collection has none', () => {
|
|
70
|
+
const yml = stringifyCollection(baseRoot, { name: 'c' });
|
|
71
|
+
expect(yml).not.toMatch(/flow:/);
|
|
72
|
+
expect(parseCollection(yml).brunoConfig.scripts?.flow).toBeUndefined();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('drops an unrecognized flow value on write', () => {
|
|
76
|
+
const yml = stringifyCollection(baseRoot, { name: 'c', scripts: { flow: 'parallel' } });
|
|
77
|
+
expect(yml).not.toMatch(/flow:/);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('preserves additionalContextRoots alongside a written flow value', () => {
|
|
81
|
+
const yml = stringifyCollection(baseRoot, {
|
|
82
|
+
name: 'c',
|
|
83
|
+
scripts: { flow: 'sequential', additionalContextRoots: ['../libs'] }
|
|
84
|
+
});
|
|
85
|
+
const { brunoConfig } = parseCollection(yml);
|
|
86
|
+
expect(brunoConfig.scripts?.flow).toBe('sequential');
|
|
87
|
+
expect(brunoConfig.scripts?.additionalContextRoots).toEqual(['../libs']);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('stringifyCollection — client certificates', () => {
|
|
92
|
+
const baseRoot = {
|
|
93
|
+
meta: null,
|
|
94
|
+
request: { headers: [], auth: { mode: 'none' }, script: { req: null, res: null }, tests: null, vars: { req: [], res: [] } },
|
|
95
|
+
docs: null
|
|
96
|
+
} as any;
|
|
97
|
+
|
|
98
|
+
it('round-trips cert/pfx certificates', () => {
|
|
99
|
+
const brunoConfig = {
|
|
100
|
+
name: 'c',
|
|
101
|
+
clientCertificates: {
|
|
102
|
+
certs: [
|
|
103
|
+
{
|
|
104
|
+
domain: 'localhost',
|
|
105
|
+
type: 'cert',
|
|
106
|
+
certFilePath: './certs/client-cert.pem',
|
|
107
|
+
keyFilePath: './certs/client-key.pem',
|
|
108
|
+
passphrase: 'secret'
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
domain: 'example.com',
|
|
112
|
+
type: 'pfx',
|
|
113
|
+
pfxFilePath: './certs/client.pfx',
|
|
114
|
+
passphrase: ''
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const yml = stringifyCollection(baseRoot, brunoConfig);
|
|
121
|
+
const { brunoConfig: reparsed } = parseCollection(yml);
|
|
122
|
+
|
|
123
|
+
expect(reparsed.clientCertificates.certs).toEqual([
|
|
124
|
+
{
|
|
125
|
+
domain: 'localhost',
|
|
126
|
+
type: 'cert',
|
|
127
|
+
certFilePath: './certs/client-cert.pem',
|
|
128
|
+
keyFilePath: './certs/client-key.pem',
|
|
129
|
+
passphrase: 'secret'
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
domain: 'example.com',
|
|
133
|
+
type: 'pfx',
|
|
134
|
+
pfxFilePath: './certs/client.pfx',
|
|
135
|
+
passphrase: ''
|
|
136
|
+
}
|
|
137
|
+
]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('writes disabled: true for disabled certs and round-trips it', () => {
|
|
141
|
+
const brunoConfig = {
|
|
142
|
+
name: 'c',
|
|
143
|
+
clientCertificates: {
|
|
144
|
+
certs: [
|
|
145
|
+
{
|
|
146
|
+
domain: 'localhost',
|
|
147
|
+
type: 'cert',
|
|
148
|
+
certFilePath: './certs/client-cert.pem',
|
|
149
|
+
keyFilePath: './certs/client-key.pem',
|
|
150
|
+
passphrase: 'secret',
|
|
151
|
+
disabled: true
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
domain: 'example.com',
|
|
155
|
+
type: 'pfx',
|
|
156
|
+
pfxFilePath: './certs/client.pfx',
|
|
157
|
+
passphrase: '',
|
|
158
|
+
disabled: true
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const yml = stringifyCollection(baseRoot, brunoConfig);
|
|
165
|
+
|
|
166
|
+
expect(yml).toMatch(/disabled:\s*true/);
|
|
167
|
+
|
|
168
|
+
const { brunoConfig: reparsed } = parseCollection(yml);
|
|
169
|
+
expect(reparsed.clientCertificates.certs[0].disabled).toBe(true);
|
|
170
|
+
expect(reparsed.clientCertificates.certs[1].disabled).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('does not write disabled for enabled certs', () => {
|
|
174
|
+
const brunoConfig = {
|
|
175
|
+
name: 'c',
|
|
176
|
+
clientCertificates: {
|
|
177
|
+
certs: [
|
|
178
|
+
{
|
|
179
|
+
domain: 'example.com',
|
|
180
|
+
type: 'pfx',
|
|
181
|
+
pfxFilePath: './certs/client.pfx',
|
|
182
|
+
passphrase: ''
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const yml = stringifyCollection(baseRoot, brunoConfig);
|
|
189
|
+
|
|
190
|
+
expect(yml).not.toMatch(/disabled:/);
|
|
191
|
+
|
|
192
|
+
const { brunoConfig: reparsed } = parseCollection(yml);
|
|
193
|
+
expect(reparsed.clientCertificates.certs[0]).not.toHaveProperty('disabled');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('stringifyCollection — writing the collection version', () => {
|
|
198
|
+
const baseRoot = {
|
|
199
|
+
meta: null,
|
|
200
|
+
request: { headers: [], auth: { mode: 'none' }, script: { req: null, res: null }, tests: null, vars: { req: [], res: [] } },
|
|
201
|
+
docs: null
|
|
202
|
+
} as any;
|
|
203
|
+
|
|
204
|
+
it('writes the version as-is and reads back the same value', () => {
|
|
205
|
+
const yml = stringifyCollection(baseRoot, { name: 'c', version: '2' });
|
|
206
|
+
expect(yml).toMatch(/version:\s*['"]?2['"]?/);
|
|
207
|
+
expect(parseCollection(yml).brunoConfig.version).toBe('2');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('writes no version when the collection has none', () => {
|
|
211
|
+
const yml = stringifyCollection(baseRoot, { name: 'c' });
|
|
212
|
+
expect(parseCollection(yml).brunoConfig.version).toBeUndefined();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('accepts any version text — there is no check on the format', () => {
|
|
216
|
+
// The version is free-form: users can set whatever they like. Each of these should be
|
|
217
|
+
// saved and read back exactly the same, without changing the format or rejecting it.
|
|
218
|
+
['v1.0.0', '2', '1.2.3-beta', '2024.01.15', 'release-2024-Q3', 'anything-goes'].forEach((version) => {
|
|
219
|
+
const yml = stringifyCollection(baseRoot, { name: 'c', version });
|
|
220
|
+
expect(parseCollection(yml).brunoConfig.version).toBe(version);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
});
|
|
@@ -61,7 +61,8 @@ const hasRequestScripts = (collectionRoot: any): boolean => {
|
|
|
61
61
|
|
|
62
62
|
const hasPresets = (brunoConfig: any): boolean => {
|
|
63
63
|
return brunoConfig?.presets?.requestType?.length
|
|
64
|
-
|| brunoConfig?.presets?.requestUrl?.length
|
|
64
|
+
|| brunoConfig?.presets?.requestUrl?.length
|
|
65
|
+
|| brunoConfig?.presets?.defaultEnvironment?.length;
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
const stringifyCollection = (collectionRoot: any, brunoConfig: any): string => {
|
|
@@ -73,6 +74,10 @@ const stringifyCollection = (collectionRoot: any, brunoConfig: any): string => {
|
|
|
73
74
|
name: brunoConfig.name || 'Untitled Collection'
|
|
74
75
|
};
|
|
75
76
|
|
|
77
|
+
if (brunoConfig.version != null && brunoConfig.version !== '') {
|
|
78
|
+
oc.info.version = String(brunoConfig.version);
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
// collection config
|
|
77
82
|
if (hasCollectionConfig(brunoConfig)) {
|
|
78
83
|
oc.config = {};
|
|
@@ -144,7 +149,8 @@ const stringifyCollection = (collectionRoot: any, brunoConfig: any): string => {
|
|
|
144
149
|
type: 'pem',
|
|
145
150
|
certificateFilePath: cert.certFilePath,
|
|
146
151
|
privateKeyFilePath: cert.keyFilePath,
|
|
147
|
-
...(cert.passphrase && { passphrase: cert.passphrase })
|
|
152
|
+
...(cert.passphrase && { passphrase: cert.passphrase }),
|
|
153
|
+
...(cert.disabled === true && { disabled: true })
|
|
148
154
|
};
|
|
149
155
|
return pemCert;
|
|
150
156
|
} else if (cert.type === 'pfx') {
|
|
@@ -152,7 +158,8 @@ const stringifyCollection = (collectionRoot: any, brunoConfig: any): string => {
|
|
|
152
158
|
domain: cert.domain,
|
|
153
159
|
type: 'pkcs12',
|
|
154
160
|
pkcs12FilePath: cert.pfxFilePath,
|
|
155
|
-
...(cert.passphrase && { passphrase: cert.passphrase })
|
|
161
|
+
...(cert.passphrase && { passphrase: cert.passphrase }),
|
|
162
|
+
...(cert.disabled === true && { disabled: true })
|
|
156
163
|
};
|
|
157
164
|
return pkcs12Cert;
|
|
158
165
|
} else {
|
|
@@ -237,6 +244,7 @@ const stringifyCollection = (collectionRoot: any, brunoConfig: any): string => {
|
|
|
237
244
|
}
|
|
238
245
|
|
|
239
246
|
if (hasPresets(brunoConfig)) {
|
|
247
|
+
const presetsExtension: any = {};
|
|
240
248
|
const presetsRequest: any = {};
|
|
241
249
|
if (brunoConfig.presets.requestType?.length) {
|
|
242
250
|
presetsRequest.type = brunoConfig.presets.requestType;
|
|
@@ -244,22 +252,31 @@ const stringifyCollection = (collectionRoot: any, brunoConfig: any): string => {
|
|
|
244
252
|
if (brunoConfig.presets.requestUrl?.length) {
|
|
245
253
|
presetsRequest.url = brunoConfig.presets.requestUrl;
|
|
246
254
|
}
|
|
247
|
-
|
|
248
|
-
request
|
|
249
|
-
}
|
|
255
|
+
if (Object.keys(presetsRequest).length) {
|
|
256
|
+
presetsExtension.request = presetsRequest;
|
|
257
|
+
}
|
|
258
|
+
if (brunoConfig.presets.defaultEnvironment?.length) {
|
|
259
|
+
presetsExtension.defaultEnvironment = brunoConfig.presets.defaultEnvironment;
|
|
260
|
+
}
|
|
261
|
+
brunoExtension.presets = presetsExtension;
|
|
250
262
|
}
|
|
251
263
|
|
|
252
264
|
oc.extensions.bruno = brunoExtension;
|
|
253
265
|
}
|
|
254
266
|
|
|
255
267
|
// bruno-specific script extensions
|
|
268
|
+
const brunoScripts: Record<string, unknown> = {};
|
|
256
269
|
if (brunoConfig.scripts?.additionalContextRoots?.length) {
|
|
270
|
+
brunoScripts.additionalContextRoots = brunoConfig.scripts.additionalContextRoots;
|
|
271
|
+
}
|
|
272
|
+
if (brunoConfig.scripts?.flow === 'sandwich' || brunoConfig.scripts?.flow === 'sequential') {
|
|
273
|
+
brunoScripts.flow = brunoConfig.scripts.flow;
|
|
274
|
+
}
|
|
275
|
+
if (Object.keys(brunoScripts).length > 0) {
|
|
257
276
|
if (!oc.extensions.bruno) {
|
|
258
277
|
oc.extensions.bruno = {};
|
|
259
278
|
}
|
|
260
|
-
(oc.extensions.bruno as any).scripts =
|
|
261
|
-
additionalContextRoots: brunoConfig.scripts.additionalContextRoots
|
|
262
|
-
};
|
|
279
|
+
(oc.extensions.bruno as any).scripts = brunoScripts;
|
|
263
280
|
}
|
|
264
281
|
|
|
265
282
|
// bruno-specific extensions
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import stringifyEnvironment, { toOpenCollectionEnvironmentVariables } from './stringifyEnvironment';
|
|
2
|
+
import parseEnvironment from './parseEnvironment';
|
|
3
|
+
|
|
4
|
+
describe('toOpenCollectionEnvironmentVariables', () => {
|
|
5
|
+
it('returns undefined for null / empty input', () => {
|
|
6
|
+
expect(toOpenCollectionEnvironmentVariables([])).toBeUndefined();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('serializes plain string variables as raw strings', () => {
|
|
10
|
+
const out = toOpenCollectionEnvironmentVariables([
|
|
11
|
+
{ uid: 'u1', name: 'apiKey', value: 'abc', type: 'text', enabled: true, secret: false } as any
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
expect(out).toEqual([{ name: 'apiKey', value: 'abc' }]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('serializes typed variables as a {type, data} struct', () => {
|
|
18
|
+
const out = toOpenCollectionEnvironmentVariables([
|
|
19
|
+
{ uid: 'u1', name: 'port', value: 300, type: 'text', enabled: true, secret: false, dataType: 'number' } as any,
|
|
20
|
+
{ uid: 'u2', name: 'flag', value: true, type: 'text', enabled: true, secret: false, dataType: 'boolean' } as any,
|
|
21
|
+
{ uid: 'u3', name: 'config', value: { a: 1 }, type: 'text', enabled: true, secret: false, dataType: 'object' } as any
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
expect(out).toEqual([
|
|
25
|
+
{ name: 'port', value: { type: 'number', data: '300' } },
|
|
26
|
+
{ name: 'flag', value: { type: 'boolean', data: 'true' } },
|
|
27
|
+
{ name: 'config', value: { type: 'object', data: '{\n "a": 1\n}' } }
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('serializes secret variables without a value but preserves the dataType as `type`', () => {
|
|
32
|
+
const out = toOpenCollectionEnvironmentVariables([
|
|
33
|
+
{ uid: 'u1', name: 'apiKey', value: '', type: 'text', enabled: true, secret: true } as any,
|
|
34
|
+
{ uid: 'u2', name: 'flag', value: '', type: 'text', enabled: false, secret: true, dataType: 'number' } as any
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
expect(out).toEqual([
|
|
38
|
+
{ secret: true, name: 'apiKey' },
|
|
39
|
+
{ secret: true, name: 'flag', type: 'number', disabled: true }
|
|
40
|
+
]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('marks disabled non-secret variables', () => {
|
|
44
|
+
const out = toOpenCollectionEnvironmentVariables([
|
|
45
|
+
{ uid: 'u1', name: 'apiKey', value: 'abc', type: 'text', enabled: false, secret: false } as any
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
expect(out).toEqual([{ name: 'apiKey', value: 'abc', disabled: true }]);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('stringifyEnvironment', () => {
|
|
53
|
+
it('round-trips through parseEnvironment for typed and secret variables', () => {
|
|
54
|
+
const env = {
|
|
55
|
+
uid: 'env-uid',
|
|
56
|
+
name: 'test_env',
|
|
57
|
+
variables: [
|
|
58
|
+
{ uid: 'u1', name: 'env_str', value: 'hello', type: 'text', enabled: true, secret: false },
|
|
59
|
+
{ uid: 'u2', name: 'env_num', value: 300, type: 'text', enabled: true, secret: false, dataType: 'number' as const },
|
|
60
|
+
{ uid: 'u3', name: 'env_bool', value: true, type: 'text', enabled: true, secret: false, dataType: 'boolean' as const },
|
|
61
|
+
{ uid: 'u4', name: 'env_obj', value: { scope: 'env' }, type: 'text', enabled: true, secret: false, dataType: 'object' as const },
|
|
62
|
+
{ uid: 'u5', name: 'env_secret', value: '', type: 'text', enabled: true, secret: true },
|
|
63
|
+
{ uid: 'u6', name: 'env_secret_num', value: '', type: 'text', enabled: true, secret: true, dataType: 'number' as const }
|
|
64
|
+
]
|
|
65
|
+
} as any;
|
|
66
|
+
|
|
67
|
+
const yml = stringifyEnvironment(env);
|
|
68
|
+
const reparsed = parseEnvironment(yml);
|
|
69
|
+
|
|
70
|
+
expect(reparsed.name).toBe('test_env');
|
|
71
|
+
expect(reparsed.variables[0]).toMatchObject({ name: 'env_str', value: 'hello', secret: false });
|
|
72
|
+
expect(reparsed.variables[1]).toMatchObject({ name: 'env_num', value: 300, dataType: 'number', secret: false });
|
|
73
|
+
expect(reparsed.variables[2]).toMatchObject({ name: 'env_bool', value: true, dataType: 'boolean', secret: false });
|
|
74
|
+
expect(reparsed.variables[3]).toMatchObject({ name: 'env_obj', value: { scope: 'env' }, dataType: 'object', secret: false });
|
|
75
|
+
expect(reparsed.variables[4]).toMatchObject({ name: 'env_secret', secret: true, value: '' });
|
|
76
|
+
expect(reparsed.variables[4].dataType).toBeUndefined();
|
|
77
|
+
expect(reparsed.variables[5]).toMatchObject({ name: 'env_secret_num', secret: true, value: '', dataType: 'number' });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('preserves the color field when present', () => {
|
|
81
|
+
const env = {
|
|
82
|
+
uid: 'env-uid',
|
|
83
|
+
name: 'colorful',
|
|
84
|
+
color: '#ff0000',
|
|
85
|
+
variables: []
|
|
86
|
+
} as any;
|
|
87
|
+
|
|
88
|
+
const reparsed = parseEnvironment(stringifyEnvironment(env));
|
|
89
|
+
expect(reparsed.color).toBe('#ff0000');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -1,53 +1,89 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Environment as BrunoEnvironment,
|
|
3
|
+
EnvironmentVariable as BrunoEnvironmentVariable,
|
|
4
|
+
SecretEnvironmentVariable as BrunoSecretEnvironmentVariable,
|
|
5
|
+
ExternalSecretProviderType as BrunoExternalSecretProviderType
|
|
6
|
+
} from '@usebruno/schema-types/collection/environment';
|
|
2
7
|
import type { Environment } from '@opencollection/types/config/environments';
|
|
3
8
|
import type { Variable, SecretVariable } from '@opencollection/types/common/variables';
|
|
4
9
|
import { stringifyYml } from './utils';
|
|
5
10
|
import { ensureString } from '../../utils';
|
|
11
|
+
import { SECRET_PROVIDER_IDENTIFIER_FIELDS } from '../../constants';
|
|
12
|
+
import { hasTypedMetadata, toOpenCollectionTypedValue, serializeVariableValue } from './common/datatype';
|
|
6
13
|
|
|
7
|
-
const toOpenCollectionEnvironmentVariables = (variables: BrunoEnvironmentVariable[]): (Variable | SecretVariable)[] | undefined => {
|
|
14
|
+
export const toOpenCollectionEnvironmentVariables = (variables: (BrunoEnvironmentVariable | BrunoSecretEnvironmentVariable)[]): (Variable | SecretVariable)[] | undefined => {
|
|
8
15
|
if (!variables?.length) {
|
|
9
16
|
return undefined;
|
|
10
17
|
}
|
|
11
18
|
|
|
12
19
|
const ocVariables: (Variable | SecretVariable)[] = variables
|
|
13
|
-
.
|
|
14
|
-
|
|
15
|
-
// update this when bruno app supports non-string values
|
|
16
|
-
return typeof v.value === 'string';
|
|
17
|
-
})
|
|
18
|
-
.map((v: BrunoEnvironmentVariable): Variable | SecretVariable => {
|
|
19
|
-
if (v.secret === true) {
|
|
20
|
+
.map((v): Variable | SecretVariable => {
|
|
21
|
+
if ('secret' in v && v.secret === true) {
|
|
20
22
|
const secretVar: SecretVariable = {
|
|
21
23
|
secret: true,
|
|
22
24
|
name: v.name || ''
|
|
23
25
|
};
|
|
24
|
-
if (v
|
|
25
|
-
secretVar.type = v.
|
|
26
|
+
if (hasTypedMetadata(v)) {
|
|
27
|
+
secretVar.type = v.dataType;
|
|
26
28
|
}
|
|
27
29
|
if (v.enabled === false) {
|
|
28
30
|
secretVar.disabled = true;
|
|
29
31
|
}
|
|
32
|
+
if (v.description !== undefined) {
|
|
33
|
+
secretVar.description = v.description;
|
|
34
|
+
}
|
|
30
35
|
return secretVar;
|
|
31
36
|
}
|
|
32
37
|
|
|
38
|
+
const valueStr = serializeVariableValue(v.value);
|
|
39
|
+
|
|
33
40
|
const variable: Variable = {
|
|
34
41
|
name: v.name || '',
|
|
35
|
-
value:
|
|
36
|
-
v.datatype && v.datatype !== 'string'
|
|
37
|
-
? { type: v.datatype, data: ensureString(v.value) }
|
|
38
|
-
: ensureString(v.value)
|
|
42
|
+
value: hasTypedMetadata(v) ? toOpenCollectionTypedValue(v, valueStr) : valueStr
|
|
39
43
|
};
|
|
40
44
|
|
|
41
45
|
if (v.enabled === false) {
|
|
42
46
|
variable.disabled = true;
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
if (v.description !== undefined) {
|
|
50
|
+
variable.description = v.description;
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
return variable;
|
|
46
54
|
});
|
|
47
55
|
|
|
48
56
|
return ocVariables.length > 0 ? ocVariables : undefined;
|
|
49
57
|
};
|
|
50
58
|
|
|
59
|
+
// Serializes the runtime externalSecrets block to yml `{ type, variables: [{ name,
|
|
60
|
+
// path|secretName|vaultName, disabled? }] }`. Runtime-only fields (uid, value,
|
|
61
|
+
// providerConfig, lastSyncedAt) are never written.
|
|
62
|
+
const toOpenCollectionExternalSecrets = (externalSecrets: BrunoEnvironment['externalSecrets']): { type: string; variables: Record<string, string | boolean>[] } | undefined => {
|
|
63
|
+
const type = externalSecrets?.type as BrunoExternalSecretProviderType | undefined;
|
|
64
|
+
const identifierField = type && SECRET_PROVIDER_IDENTIFIER_FIELDS[type];
|
|
65
|
+
if (!type || !identifierField) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Keep any named entry, even one with an empty identifier — a half-filled secret row is the user's
|
|
70
|
+
// data; dropping it here would silently delete it on the next save.
|
|
71
|
+
const variables = (externalSecrets?.variables || [])
|
|
72
|
+
.filter((v: any) => v && v.name)
|
|
73
|
+
.map((v: any) => {
|
|
74
|
+
const secret: Record<string, string | boolean> = {
|
|
75
|
+
name: v.name,
|
|
76
|
+
[identifierField]: ensureString(v[identifierField])
|
|
77
|
+
};
|
|
78
|
+
if (v.enabled === false) {
|
|
79
|
+
secret.disabled = true;
|
|
80
|
+
}
|
|
81
|
+
return secret;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return { type, variables };
|
|
85
|
+
};
|
|
86
|
+
|
|
51
87
|
const stringifyEnvironment = (environment: BrunoEnvironment): string => {
|
|
52
88
|
try {
|
|
53
89
|
const ocEnvironment: Environment = {
|
|
@@ -65,11 +101,9 @@ const stringifyEnvironment = (environment: BrunoEnvironment): string => {
|
|
|
65
101
|
}
|
|
66
102
|
}
|
|
67
103
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
variables: (environment.externalSecrets.variables || []).map((variable) => ({ ...variable }))
|
|
72
|
-
};
|
|
104
|
+
const ocExternalSecrets = toOpenCollectionExternalSecrets(environment.externalSecrets);
|
|
105
|
+
if (ocExternalSecrets) {
|
|
106
|
+
(ocEnvironment as any).externalSecrets = ocExternalSecrets;
|
|
73
107
|
}
|
|
74
108
|
|
|
75
109
|
return stringifyYml(ocEnvironment);
|
|
@@ -78,4 +112,5 @@ const stringifyEnvironment = (environment: BrunoEnvironment): string => {
|
|
|
78
112
|
throw error;
|
|
79
113
|
}
|
|
80
114
|
};
|
|
115
|
+
|
|
81
116
|
export default stringifyEnvironment;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import stringifyFolder from './stringifyFolder';
|
|
2
|
+
import parseFolder from './parseFolder';
|
|
3
|
+
|
|
4
|
+
// Typed folder vars serialize to OC's `{ type, data }` struct.
|
|
5
|
+
// `dataType: 'string'` is the implicit default and stays a raw string.
|
|
6
|
+
|
|
7
|
+
describe('stringifyFolder — typed request.variables', () => {
|
|
8
|
+
it('round-trips typed values and omits a typed struct for the implicit string default', () => {
|
|
9
|
+
const folderRoot = {
|
|
10
|
+
meta: { name: 'my-folder', seq: 1 },
|
|
11
|
+
request: {
|
|
12
|
+
headers: [],
|
|
13
|
+
auth: { mode: 'none' },
|
|
14
|
+
script: { req: null, res: null },
|
|
15
|
+
tests: null,
|
|
16
|
+
vars: {
|
|
17
|
+
req: [
|
|
18
|
+
{ uid: 'v1', name: 'count', value: 42, enabled: true, dataType: 'number' },
|
|
19
|
+
{ uid: 'v2', name: 'enabled', value: true, enabled: true, dataType: 'boolean' },
|
|
20
|
+
{ uid: 'v3', name: 'config', value: { a: 1 }, enabled: true, dataType: 'object' },
|
|
21
|
+
{ uid: 'v4', name: 'greeting', value: 'hi', enabled: true, dataType: 'string' },
|
|
22
|
+
{ uid: 'v5', name: 'plain', value: 'hello', enabled: true }
|
|
23
|
+
],
|
|
24
|
+
res: []
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
docs: null
|
|
28
|
+
} as any;
|
|
29
|
+
|
|
30
|
+
const yml = stringifyFolder(folderRoot);
|
|
31
|
+
|
|
32
|
+
// `type: string` is never written out.
|
|
33
|
+
expect(yml).not.toMatch(/type:\s*string/);
|
|
34
|
+
|
|
35
|
+
const reparsed = parseFolder(yml);
|
|
36
|
+
const reqVars = reparsed.request!.vars!.req!;
|
|
37
|
+
|
|
38
|
+
expect(reqVars).toHaveLength(5);
|
|
39
|
+
expect(reqVars[0]).toMatchObject({ name: 'count', value: 42, dataType: 'number' });
|
|
40
|
+
expect(reqVars[1]).toMatchObject({ name: 'enabled', value: true, dataType: 'boolean' });
|
|
41
|
+
expect(reqVars[2]).toMatchObject({ name: 'config', value: { a: 1 }, dataType: 'object' });
|
|
42
|
+
expect(reqVars[3]).toMatchObject({ name: 'greeting', value: 'hi' });
|
|
43
|
+
expect(reqVars[3].dataType).toBeUndefined();
|
|
44
|
+
expect(reqVars[4]).toMatchObject({ name: 'plain', value: 'hello' });
|
|
45
|
+
expect(reqVars[4].dataType).toBeUndefined();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('stringifyFolder — seq', () => {
|
|
50
|
+
it('omits seq when the folder has none', () => {
|
|
51
|
+
const folderRoot = { meta: { name: 'no-seq-folder' }, docs: null } as any;
|
|
52
|
+
|
|
53
|
+
const yml = stringifyFolder(folderRoot);
|
|
54
|
+
const { meta } = parseFolder(yml);
|
|
55
|
+
|
|
56
|
+
expect(yml).not.toMatch(/seq:/);
|
|
57
|
+
expect(meta).toEqual(expect.objectContaining({ name: 'no-seq-folder' }));
|
|
58
|
+
expect(meta!.seq).toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('preserves an explicit numeric seq', () => {
|
|
62
|
+
const folderRoot = { meta: { name: 'ordered-folder', seq: 3 }, docs: null } as any;
|
|
63
|
+
|
|
64
|
+
const yml = stringifyFolder(folderRoot);
|
|
65
|
+
const { meta } = parseFolder(yml);
|
|
66
|
+
|
|
67
|
+
expect(meta).toEqual(expect.objectContaining({ name: 'ordered-folder' }));
|
|
68
|
+
expect(meta!.seq).toBe(3);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -40,9 +40,14 @@ const stringifyFolder = (folderRoot: FolderRoot): string => {
|
|
|
40
40
|
// info block
|
|
41
41
|
const info: FolderInfo = {
|
|
42
42
|
name: folderRoot.meta?.name || 'Untitled Folder',
|
|
43
|
-
type: 'folder'
|
|
44
|
-
seq: folderRoot.meta?.seq || 1
|
|
43
|
+
type: 'folder'
|
|
45
44
|
};
|
|
45
|
+
// Only write seq when the folder actually has a numeric one. Defaulting to 1 would
|
|
46
|
+
// force every seq-less folder into position 1 on disk and break alphabetical fallback.
|
|
47
|
+
const seq = folderRoot.meta?.seq;
|
|
48
|
+
if (typeof seq === 'number' && Number.isFinite(seq)) {
|
|
49
|
+
info.seq = seq;
|
|
50
|
+
}
|
|
46
51
|
ocFolder.info = info;
|
|
47
52
|
|
|
48
53
|
// request defaults
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import stringifyItem from './stringifyItem';
|
|
2
|
+
import parseItem from './parseItem';
|
|
3
|
+
|
|
4
|
+
// Typed bruno vars on `request.vars.req` serialize to OC's `{ type, data }`
|
|
5
|
+
// struct. `dataType: 'string'` is the implicit default and stays a raw string.
|
|
6
|
+
|
|
7
|
+
describe('stringifyItem — typed runtime.variables', () => {
|
|
8
|
+
it('round-trips typed values and omits a typed struct for the implicit string default', () => {
|
|
9
|
+
const item = {
|
|
10
|
+
uid: 'i1',
|
|
11
|
+
type: 'http-request',
|
|
12
|
+
name: 'r',
|
|
13
|
+
seq: 1,
|
|
14
|
+
request: {
|
|
15
|
+
url: 'https://example.com',
|
|
16
|
+
method: 'GET',
|
|
17
|
+
headers: [],
|
|
18
|
+
params: [],
|
|
19
|
+
body: { mode: 'none' },
|
|
20
|
+
auth: { mode: 'none' },
|
|
21
|
+
script: { req: null, res: null },
|
|
22
|
+
tests: null,
|
|
23
|
+
vars: {
|
|
24
|
+
req: [
|
|
25
|
+
{ uid: 'v1', name: 'count', value: 42, enabled: true, dataType: 'number' },
|
|
26
|
+
{ uid: 'v2', name: 'enabled', value: true, enabled: true, dataType: 'boolean' },
|
|
27
|
+
{ uid: 'v3', name: 'config', value: { a: 1 }, enabled: true, dataType: 'object' },
|
|
28
|
+
{ uid: 'v4', name: 'greeting', value: 'hi', enabled: true, dataType: 'string' },
|
|
29
|
+
{ uid: 'v5', name: 'plain', value: 'hello', enabled: true }
|
|
30
|
+
],
|
|
31
|
+
res: []
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} as any;
|
|
35
|
+
|
|
36
|
+
const yml = stringifyItem(item);
|
|
37
|
+
|
|
38
|
+
// `type: string` is never written out.
|
|
39
|
+
expect(yml).not.toMatch(/type:\s*string/);
|
|
40
|
+
|
|
41
|
+
const reparsed = parseItem(yml);
|
|
42
|
+
const reqVars = reparsed.request!.vars!.req!;
|
|
43
|
+
|
|
44
|
+
expect(reqVars).toHaveLength(5);
|
|
45
|
+
expect(reqVars[0]).toMatchObject({ name: 'count', value: 42, dataType: 'number' });
|
|
46
|
+
expect(reqVars[1]).toMatchObject({ name: 'enabled', value: true, dataType: 'boolean' });
|
|
47
|
+
expect(reqVars[2]).toMatchObject({ name: 'config', value: { a: 1 }, dataType: 'object' });
|
|
48
|
+
expect(reqVars[3]).toMatchObject({ name: 'greeting', value: 'hi' });
|
|
49
|
+
expect(reqVars[3].dataType).toBeUndefined();
|
|
50
|
+
expect(reqVars[4]).toMatchObject({ name: 'plain', value: 'hello' });
|
|
51
|
+
expect(reqVars[4].dataType).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
});
|