@hubspot/ui-extensions-dev-server 0.2.0 → 0.3.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/cli/run.js +36 -0
- package/cli/userInput.js +32 -0
- package/index.js +9 -29
- package/lib/build.js +90 -0
- package/lib/config.js +70 -0
- package/{constants.js → lib/constants.js} +7 -8
- package/lib/dev.js +89 -0
- package/lib/extensions.js +9 -0
- package/{plugins → lib/plugins}/devBuildPlugin.js +55 -14
- package/{plugins → lib/plugins}/manifestPlugin.js +1 -2
- package/{server.js → lib/server.js} +8 -6
- package/lib/utils.js +20 -0
- package/package.json +25 -23
- package/build.js +0 -60
- package/config.js +0 -129
- package/dev.js +0 -64
- package/run.js +0 -41
- package/tests/runTests.js +0 -50
- package/tests/testBuild.js +0 -82
- package/tests/testDevServer.js +0 -287
- package/tests/utils.js +0 -76
- package/utils.js +0 -10
- /package/{logger.js → cli/logger.js} +0 -0
- /package/{cli.js → cli/utils.js} +0 -0
package/tests/testDevServer.js
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
const { spawn } = require('child_process');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const assert = require('assert');
|
|
5
|
-
const axios = require('axios');
|
|
6
|
-
const WebSocket = require('ws');
|
|
7
|
-
const {
|
|
8
|
-
VITE_DEFAULT_PORT,
|
|
9
|
-
WEBSOCKET_PORT,
|
|
10
|
-
WEBSOCKET_MESSAGE_VERSION,
|
|
11
|
-
EXTENSIONS_MESSAGE_VERSION,
|
|
12
|
-
} = require('../constants');
|
|
13
|
-
const { verifyFileContents, generateManifestOutputPair } = require('./utils');
|
|
14
|
-
const { getUrlSafeFileName } = require('../utils');
|
|
15
|
-
|
|
16
|
-
const preShutdownResults = {
|
|
17
|
-
buildTestPassed: false,
|
|
18
|
-
expressStaticTestPassed: false,
|
|
19
|
-
extensionsEndpointPassed: false,
|
|
20
|
-
webSocketTestPassed: false,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const postShutdownResults = {
|
|
24
|
-
websocketShutdownReceived: false,
|
|
25
|
-
expressServerShutdown: false,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const host = 'http://hslocal.net';
|
|
29
|
-
|
|
30
|
-
let running = false;
|
|
31
|
-
|
|
32
|
-
function resetResults() {
|
|
33
|
-
Object.keys(preShutdownResults).forEach(result => {
|
|
34
|
-
preShutdownResults[result] = false;
|
|
35
|
-
});
|
|
36
|
-
Object.keys(postShutdownResults).forEach(result => {
|
|
37
|
-
postShutdownResults[result] = false;
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function testDevServer(logger, devServerProcess, specs) {
|
|
42
|
-
const devServerInterval = setInterval(runDevServer, 1000);
|
|
43
|
-
let runCount = 0;
|
|
44
|
-
|
|
45
|
-
function runDevServer() {
|
|
46
|
-
if (running === false && !devServerProcess) {
|
|
47
|
-
resetResults();
|
|
48
|
-
_testDevServer(logger, devServerProcess, specs[runCount]);
|
|
49
|
-
runCount += 1;
|
|
50
|
-
}
|
|
51
|
-
if (runCount === specs.length) {
|
|
52
|
-
clearInterval(devServerInterval);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function _testDevServer(logger, devServerProcess, spec) {
|
|
58
|
-
running = true;
|
|
59
|
-
const { entrypoint, expected } = spec;
|
|
60
|
-
|
|
61
|
-
const filename = getUrlSafeFileName(entrypoint);
|
|
62
|
-
const localExpectations = {
|
|
63
|
-
callback: `${host}:${VITE_DEFAULT_PORT}/${filename}`,
|
|
64
|
-
websocketUrl: `ws://localhost:${WEBSOCKET_PORT}`,
|
|
65
|
-
...expected,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
logger.warn(`\nDev Server Tests started - ${entrypoint} 🤞`);
|
|
69
|
-
// We need to use spawn here because it will put the process into the background,
|
|
70
|
-
// which is required because dev mode is a blocking process and we want to test that
|
|
71
|
-
// the express server and websocket server are starting properly
|
|
72
|
-
devServerProcess = spawn('hs-ui-extensions-dev-server', [
|
|
73
|
-
'dev',
|
|
74
|
-
'--extension',
|
|
75
|
-
entrypoint,
|
|
76
|
-
]);
|
|
77
|
-
|
|
78
|
-
devServerProcess.stdout.on('data', buffer => {
|
|
79
|
-
const data = buffer.toString().toLowerCase();
|
|
80
|
-
console.debug('[Dev Server]:', data);
|
|
81
|
-
if (data.includes('built in')) {
|
|
82
|
-
testBuild(logger, entrypoint);
|
|
83
|
-
}
|
|
84
|
-
if (
|
|
85
|
-
data.includes('listening') &&
|
|
86
|
-
data.includes(`${host}:${VITE_DEFAULT_PORT}/extensions`)
|
|
87
|
-
) {
|
|
88
|
-
setTimeout(() => {
|
|
89
|
-
testExpressServer(logger, localExpectations, filename);
|
|
90
|
-
testWebSocketServer(logger, localExpectations);
|
|
91
|
-
}, 1000);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// If the dev server writes to stderr, log the error and throw a new error
|
|
96
|
-
devServerProcess.stderr.on('data', buffer => {
|
|
97
|
-
const data = buffer.toString();
|
|
98
|
-
logger.error(data.toString());
|
|
99
|
-
throw new Error(data);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// When the process closes make sure we met all the success conditions
|
|
103
|
-
devServerProcess.on('close', () => {
|
|
104
|
-
if (metPreShutdownConditions()) {
|
|
105
|
-
logger.info(`- Dev Server pre-shutdown tests passed - ${entrypoint} 🚀`);
|
|
106
|
-
testPostShutdown(logger, filename);
|
|
107
|
-
} else {
|
|
108
|
-
console.log(preShutdownResults);
|
|
109
|
-
logger.error('Tests failed 😭');
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const preShutdownInterval = setInterval(preShutdownCallback, 1000);
|
|
114
|
-
let preShutdownCheckCount = 0;
|
|
115
|
-
function preShutdownCallback() {
|
|
116
|
-
preShutdownCheckCount += 1;
|
|
117
|
-
if (metPreShutdownConditions() || preShutdownCheckCount === 5) {
|
|
118
|
-
clearInterval(preShutdownInterval);
|
|
119
|
-
devServerProcess.kill();
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function metPreShutdownConditions() {
|
|
125
|
-
const {
|
|
126
|
-
buildTestPassed,
|
|
127
|
-
expressStaticTestPassed,
|
|
128
|
-
extensionsEndpointPassed,
|
|
129
|
-
webSocketTestPassed,
|
|
130
|
-
} = preShutdownResults;
|
|
131
|
-
return (
|
|
132
|
-
buildTestPassed &&
|
|
133
|
-
expressStaticTestPassed &&
|
|
134
|
-
extensionsEndpointPassed &&
|
|
135
|
-
webSocketTestPassed
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function metPostShutdownConditions() {
|
|
140
|
-
const {
|
|
141
|
-
websocketShutdownReceived,
|
|
142
|
-
expressServerShutdown,
|
|
143
|
-
} = postShutdownResults;
|
|
144
|
-
|
|
145
|
-
return websocketShutdownReceived && expressServerShutdown;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Test that the files were built in the proper location and spot
|
|
149
|
-
// check the contents of the files.
|
|
150
|
-
function testBuild(logger, entryPoint) {
|
|
151
|
-
// // Make sure the files are getting generated in the dist dir
|
|
152
|
-
const distDir = path.join(process.cwd(), 'dist');
|
|
153
|
-
const filesInOutputDir = fs.readdirSync(distDir);
|
|
154
|
-
const filename = path.parse(entryPoint).name;
|
|
155
|
-
assert.deepStrictEqual(
|
|
156
|
-
filesInOutputDir,
|
|
157
|
-
generateManifestOutputPair(entryPoint)
|
|
158
|
-
);
|
|
159
|
-
const fileContents = fs
|
|
160
|
-
.readFileSync(path.join(distDir, `${filename}.js`))
|
|
161
|
-
.toString();
|
|
162
|
-
verifyFileContents(`${filename}.js`, fileContents);
|
|
163
|
-
|
|
164
|
-
// Check for the inlined source map
|
|
165
|
-
assert(
|
|
166
|
-
fileContents.includes(
|
|
167
|
-
'//# sourceMappingURL=data:application/json;charset=utf-8;base64,'
|
|
168
|
-
)
|
|
169
|
-
);
|
|
170
|
-
logger.info('- Build succeeded 🚀');
|
|
171
|
-
preShutdownResults.buildTestPassed = true;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Test that the express server is running on the expected port
|
|
175
|
-
// and that it is serving the files as expected.
|
|
176
|
-
function testExpressServer(logger, expected, filename) {
|
|
177
|
-
axios
|
|
178
|
-
.get(`${host}:${VITE_DEFAULT_PORT}/${filename}`)
|
|
179
|
-
.then(response => {
|
|
180
|
-
assert.strictEqual(response.status, 200, 'Bundle status code');
|
|
181
|
-
logger.info('- Express server connected and serving files 🚀');
|
|
182
|
-
preShutdownResults.expressStaticTestPassed = true;
|
|
183
|
-
})
|
|
184
|
-
.catch(logger.error);
|
|
185
|
-
axios
|
|
186
|
-
.get(`${host}:${VITE_DEFAULT_PORT}/extensions`)
|
|
187
|
-
.then(response => {
|
|
188
|
-
const { data, status } = response;
|
|
189
|
-
assert.strictEqual(status, 200, '/extensions status code');
|
|
190
|
-
|
|
191
|
-
// Top level response assertions
|
|
192
|
-
assert.strictEqual(data.websocket, expected.websocketUrl);
|
|
193
|
-
assert.strictEqual(data.version, EXTENSIONS_MESSAGE_VERSION);
|
|
194
|
-
assert(data.extensions);
|
|
195
|
-
|
|
196
|
-
// Extension level response assertions
|
|
197
|
-
assert.equal(data.extensions.length, 1, '/extensions body length');
|
|
198
|
-
const extension = data.extensions.pop();
|
|
199
|
-
assert(extension.manifest);
|
|
200
|
-
assert.strictEqual(
|
|
201
|
-
extension.appName,
|
|
202
|
-
expected.appName,
|
|
203
|
-
'/extensions appName'
|
|
204
|
-
);
|
|
205
|
-
assert.strictEqual(extension.title, expected.title, '/extensions title');
|
|
206
|
-
assert.strictEqual(
|
|
207
|
-
extension.callback,
|
|
208
|
-
expected.callback,
|
|
209
|
-
'/extensions callback'
|
|
210
|
-
);
|
|
211
|
-
logger.info('- Express serving extension data 🚀');
|
|
212
|
-
preShutdownResults.extensionsEndpointPassed = true;
|
|
213
|
-
})
|
|
214
|
-
.catch(logger.error);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Test the the web socket server is running on the expected port and
|
|
218
|
-
// that we are able to receive messages from it.
|
|
219
|
-
function testWebSocketServer(logger, expected) {
|
|
220
|
-
const ws = new WebSocket(expected.websocketUrl);
|
|
221
|
-
ws.on('message', messageBuffer => {
|
|
222
|
-
const message = JSON.parse(messageBuffer.toString());
|
|
223
|
-
console.debug('[WebSocket Message]:', message);
|
|
224
|
-
// Vite sends { type: 'connected'} as a greeting on connect
|
|
225
|
-
if (message.type === 'connected') {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
assert(['start', 'shutdown'].includes(message.event), 'Message Event');
|
|
230
|
-
assert.strictEqual(message.appName, expected.appName, 'Message appName');
|
|
231
|
-
assert.strictEqual(message.title, expected.title, 'Message title');
|
|
232
|
-
assert.strictEqual(message.version, WEBSOCKET_MESSAGE_VERSION);
|
|
233
|
-
logger.info(
|
|
234
|
-
`- WebSocket server connected and sending '${message.event}' messages 🚀`
|
|
235
|
-
);
|
|
236
|
-
if (message.event === 'start') {
|
|
237
|
-
preShutdownResults.webSocketTestPassed = true;
|
|
238
|
-
} else if (message.event === 'shutdown') {
|
|
239
|
-
postShutdownResults.websocketShutdownReceived = true;
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function testPostShutdown(logger, filename) {
|
|
245
|
-
testExpressHasShutdown(filename);
|
|
246
|
-
|
|
247
|
-
const shutdownInterval = setInterval(shutdownCallback, 1000);
|
|
248
|
-
let shutdownCheckCount = 0;
|
|
249
|
-
function shutdownCallback() {
|
|
250
|
-
shutdownCheckCount += 1;
|
|
251
|
-
if (metPostShutdownConditions()) {
|
|
252
|
-
clearInterval(shutdownInterval);
|
|
253
|
-
logger.info('- Dev Server post-shutdown tests passed 🚀');
|
|
254
|
-
logger.info('Dev Server tests passed 🚀');
|
|
255
|
-
running = false;
|
|
256
|
-
} else if (shutdownCheckCount === 5) {
|
|
257
|
-
clearInterval(shutdownInterval);
|
|
258
|
-
console.log(postShutdownResults);
|
|
259
|
-
logger.error('Tests failed 😭');
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function testExpressHasShutdown(filename) {
|
|
265
|
-
axios
|
|
266
|
-
.get(`${host}:${VITE_DEFAULT_PORT}/${filename}`)
|
|
267
|
-
.then(response => {
|
|
268
|
-
console.debug(response);
|
|
269
|
-
})
|
|
270
|
-
.catch(err => {
|
|
271
|
-
assert.strictEqual(
|
|
272
|
-
err.syscall,
|
|
273
|
-
'connect',
|
|
274
|
-
'The connect call should fail'
|
|
275
|
-
);
|
|
276
|
-
assert.strictEqual(
|
|
277
|
-
err.code,
|
|
278
|
-
'ECONNREFUSED',
|
|
279
|
-
'Express should refuse connection post shutdown'
|
|
280
|
-
);
|
|
281
|
-
postShutdownResults.expressServerShutdown = true;
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
module.exports = {
|
|
286
|
-
testDevServer,
|
|
287
|
-
};
|
package/tests/utils.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const { MANIFEST_FILE } = require('../constants');
|
|
3
|
-
const { getUrlSafeFileName } = require('../utils');
|
|
4
|
-
const assert = require('assert');
|
|
5
|
-
const { loadConfig } = require('../config');
|
|
6
|
-
|
|
7
|
-
function generateManifestOutputPair(entrypoint) {
|
|
8
|
-
const fileName = path.parse(entrypoint).name;
|
|
9
|
-
return [`${fileName}-${MANIFEST_FILE}`, getUrlSafeFileName(entrypoint)];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function verifyFileContents(file, contents) {
|
|
13
|
-
if (file.endsWith(MANIFEST_FILE)) {
|
|
14
|
-
const manifest = JSON.parse(contents);
|
|
15
|
-
assert(manifest.entry);
|
|
16
|
-
assert(manifest.modules);
|
|
17
|
-
assert(manifest.modules.internal);
|
|
18
|
-
manifest.modules.internal.forEach(mod => {
|
|
19
|
-
assert(mod.module);
|
|
20
|
-
assert(mod.renderedExports);
|
|
21
|
-
});
|
|
22
|
-
assert(manifest.modules.external);
|
|
23
|
-
manifest.modules.external.forEach(mod => {
|
|
24
|
-
assert(mod.module);
|
|
25
|
-
assert(mod.renderedExports);
|
|
26
|
-
});
|
|
27
|
-
assert(manifest.package);
|
|
28
|
-
assert(manifest.package.packages);
|
|
29
|
-
} else {
|
|
30
|
-
const stringsToSpotCheck = [
|
|
31
|
-
'.createRemoteReactComponent',
|
|
32
|
-
'.createElement',
|
|
33
|
-
'hubspot.extend',
|
|
34
|
-
'React',
|
|
35
|
-
'RemoteUI',
|
|
36
|
-
];
|
|
37
|
-
stringsToSpotCheck.forEach(stringToCheck => {
|
|
38
|
-
assert(
|
|
39
|
-
contents.includes(stringToCheck),
|
|
40
|
-
`File ${file} contents should contain: "${stringToCheck}"`
|
|
41
|
-
);
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function generateSpec() {
|
|
47
|
-
const config = loadConfig();
|
|
48
|
-
const entrypoints = Object.keys(config);
|
|
49
|
-
if (entrypoints.length === 0) {
|
|
50
|
-
throw new Error(
|
|
51
|
-
'Unable to determine testable entrypoints from config files'
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const dev = entrypoints.map(entrypoint => {
|
|
56
|
-
const { data } = config[entrypoint];
|
|
57
|
-
return {
|
|
58
|
-
entrypoint,
|
|
59
|
-
expected: {
|
|
60
|
-
...data,
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
});
|
|
64
|
-
return {
|
|
65
|
-
build: {
|
|
66
|
-
entrypoints,
|
|
67
|
-
},
|
|
68
|
-
dev,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
module.exports = {
|
|
73
|
-
generateManifestOutputPair,
|
|
74
|
-
verifyFileContents,
|
|
75
|
-
generateSpec,
|
|
76
|
-
};
|
package/utils.js
DELETED
|
File without changes
|
/package/{cli.js → cli/utils.js}
RENAMED
|
File without changes
|