@hubspot/ui-extensions-dev-server 0.0.1-beta.0 → 0.0.1-beta.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/README.md +3 -1
- package/config.js +11 -10
- package/dev.js +14 -127
- package/package.json +6 -4
- package/plugins/manifestPlugin.js +8 -3
- package/server.js +142 -0
- package/tests/runTests.js +2 -1
- package/tests/testDevServer.js +51 -17
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# UI Extensions – Dev Server
|
|
2
|
+
|
|
3
|
+
Development server to run and test your HubSpot UI Extensions.
|
|
2
4
|
|
|
3
5
|
## Overview
|
|
4
6
|
This package contains the cli for running HubSpot UI extensions locally, as well as running a production build for debugging purposes.
|
package/config.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
const prompts = require('prompts');
|
|
2
2
|
const logger = require('./logger');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const { MAIN_APP_CONFIG
|
|
4
|
+
const { MAIN_APP_CONFIG } = require('./constants');
|
|
5
5
|
const { getUrlSafeFileName } = require('./utils');
|
|
6
6
|
|
|
7
7
|
async function getExtensionConfig(configuration, extension) {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const extensionOptions = Object.keys(configuration);
|
|
9
|
+
|
|
10
|
+
if (
|
|
11
|
+
(extension && configuration[extension]) ||
|
|
12
|
+
extensionOptions.length === 1
|
|
13
|
+
) {
|
|
14
|
+
const extensionToRun = extension || extensionOptions[0];
|
|
15
|
+
const { data } = configuration[extensionToRun];
|
|
10
16
|
return {
|
|
11
|
-
key:
|
|
17
|
+
key: extensionToRun,
|
|
12
18
|
name: data.title,
|
|
13
19
|
file: data?.module?.file,
|
|
14
20
|
output: data?.output,
|
|
@@ -16,7 +22,6 @@ async function getExtensionConfig(configuration, extension) {
|
|
|
16
22
|
};
|
|
17
23
|
}
|
|
18
24
|
|
|
19
|
-
const extensionOptions = Object.keys(configuration);
|
|
20
25
|
const response = await prompts(
|
|
21
26
|
[
|
|
22
27
|
{
|
|
@@ -65,10 +70,6 @@ function loadConfig() {
|
|
|
65
70
|
// will need to be ran from, the extensions directory
|
|
66
71
|
const configPath = path.join(process.cwd(), `../${MAIN_APP_CONFIG}`);
|
|
67
72
|
|
|
68
|
-
const projectConfig = _loadRequiredConfigFile(
|
|
69
|
-
path.join(process.cwd(), `../../../${PROJECT_CONFIG}`)
|
|
70
|
-
);
|
|
71
|
-
|
|
72
73
|
const mainAppConfig = _loadRequiredConfigFile(configPath);
|
|
73
74
|
|
|
74
75
|
const crmCardsSubConfigFiles = mainAppConfig?.extensions?.crm?.cards;
|
|
@@ -107,7 +108,7 @@ function loadConfig() {
|
|
|
107
108
|
outputConfig[entryPointPath].data.output = getUrlSafeFileName(
|
|
108
109
|
entryPointPath
|
|
109
110
|
);
|
|
110
|
-
outputConfig[entryPointPath].data.appName =
|
|
111
|
+
outputConfig[entryPointPath].data.appName = mainAppConfig.name;
|
|
111
112
|
} catch (e) {
|
|
112
113
|
let errorMessage = e?.message;
|
|
113
114
|
if (e?.code === 'MODULE_NOT_FOUND') {
|
package/dev.js
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const chokidar = require('chokidar');
|
|
3
|
-
const { WebSocketServer } = require('ws');
|
|
4
|
-
const http = require('http');
|
|
5
|
-
const logger = require('./logger');
|
|
6
1
|
const { build } = require('vite');
|
|
7
2
|
const { getExtensionConfig } = require('./config');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
3
|
const { ROLLUP_OPTIONS } = require('./constants');
|
|
4
|
+
const startDevServer = require('./server');
|
|
5
|
+
const manifestPlugin = require('./plugins/manifestPlugin');
|
|
11
6
|
|
|
12
|
-
async function
|
|
13
|
-
|
|
14
|
-
build({
|
|
7
|
+
async function _buildDevBundle(outputDir, extensionConfig) {
|
|
8
|
+
await build({
|
|
15
9
|
define: {
|
|
16
10
|
'process.env.NODE_ENV': JSON.stringify(
|
|
17
11
|
process.env.NODE_ENV || 'development'
|
|
@@ -33,131 +27,24 @@ async function _devBuild(config, outputDir, extension) {
|
|
|
33
27
|
formats: ['iife'],
|
|
34
28
|
fileName: () => extensionConfig?.output,
|
|
35
29
|
},
|
|
36
|
-
rollupOptions:
|
|
30
|
+
rollupOptions: {
|
|
31
|
+
...ROLLUP_OPTIONS,
|
|
32
|
+
plugins: [
|
|
33
|
+
...(ROLLUP_OPTIONS.plugins || []),
|
|
34
|
+
manifestPlugin({ minify: false, extensionConfig }),
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
37
|
outDir: outputDir,
|
|
38
38
|
emptyOutDir: true,
|
|
39
39
|
minify: false,
|
|
40
40
|
},
|
|
41
41
|
});
|
|
42
|
-
return extensionConfig;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function _startDevServer(outputDir, port, extensionConfig) {
|
|
46
|
-
const app = express();
|
|
47
|
-
const server = http.createServer(app);
|
|
48
|
-
|
|
49
|
-
// Host the OUTPUT_DIR
|
|
50
|
-
app.use(express.static(outputDir));
|
|
51
|
-
|
|
52
|
-
// Setup websocket server to send messages to browser on bundle update
|
|
53
|
-
const wss = new WebSocketServer({ server });
|
|
54
|
-
|
|
55
|
-
const callback = `http://localhost:${port}/${extensionConfig?.output}`;
|
|
56
|
-
|
|
57
|
-
function broadcast(message) {
|
|
58
|
-
if (wss.clients.size === 0) {
|
|
59
|
-
logger.warn('No browsers connected to update');
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
wss.clients.forEach(client => {
|
|
63
|
-
client.send(JSON.stringify(message));
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
wss.on('connection', client => {
|
|
68
|
-
let base64Callback;
|
|
69
|
-
try {
|
|
70
|
-
base64Callback = fs
|
|
71
|
-
.readFileSync(
|
|
72
|
-
path.join(process.cwd(), outputDir, extensionConfig?.output)
|
|
73
|
-
)
|
|
74
|
-
.toString('base64');
|
|
75
|
-
} catch (e) {
|
|
76
|
-
logger.warn(
|
|
77
|
-
'File not found:',
|
|
78
|
-
path.join(process.cwd(), outputDir, extensionConfig?.output)
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
logger.info('Browser connected and listening for bundle updates');
|
|
83
|
-
client.send(
|
|
84
|
-
JSON.stringify({
|
|
85
|
-
event: 'start',
|
|
86
|
-
appName: extensionConfig?.appName,
|
|
87
|
-
extension: extensionConfig?.name,
|
|
88
|
-
callback: base64Callback
|
|
89
|
-
? `data:text/javascript;base64,${base64Callback}`
|
|
90
|
-
: undefined,
|
|
91
|
-
})
|
|
92
|
-
);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Start the express and websocket servers
|
|
96
|
-
server.listen({ port }, () => {
|
|
97
|
-
logger.warn(`Listening at ${callback}`);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// Setup a watcher on the dist directory and update broadcast
|
|
101
|
-
//to all clients when an event is observed
|
|
102
|
-
chokidar.watch(outputDir).on('change', file => {
|
|
103
|
-
const base64Callback = fs
|
|
104
|
-
.readFileSync(path.join(process.cwd(), file))
|
|
105
|
-
.toString('base64');
|
|
106
|
-
logger.debug(`${file} updated, reloading extension`);
|
|
107
|
-
broadcast({
|
|
108
|
-
event: 'update',
|
|
109
|
-
appName: extensionConfig?.appName,
|
|
110
|
-
extension: extensionConfig?.name,
|
|
111
|
-
callback: `data:text/javascript;base64,${base64Callback}`,
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
_configureShutDownHandlers(wss, broadcast, extensionConfig, server);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function _configureShutDownHandlers(wss, broadcast, extensionConfig, server) {
|
|
119
|
-
function shutdown() {
|
|
120
|
-
logger.warn('\nSending shutdown signal to connected browser');
|
|
121
|
-
broadcast({
|
|
122
|
-
event: 'shutdown',
|
|
123
|
-
appName: extensionConfig?.appName,
|
|
124
|
-
extension: extensionConfig?.name,
|
|
125
|
-
});
|
|
126
|
-
logger.warn('\nCleaning up after ourselves...\n');
|
|
127
|
-
|
|
128
|
-
// Terminate all active connections, close seems to hang otherwise
|
|
129
|
-
wss.clients.forEach(client => {
|
|
130
|
-
client.terminate();
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// Shut down the WebSocket server first since it is connected to the express server
|
|
134
|
-
wss.close(webSocketError => {
|
|
135
|
-
if (webSocketError) {
|
|
136
|
-
logger.error(
|
|
137
|
-
`WebSocket Server unable to shutdown correctly, ${webSocketError}`
|
|
138
|
-
);
|
|
139
|
-
} else {
|
|
140
|
-
logger.warn('WebSocket Server stopped');
|
|
141
|
-
}
|
|
142
|
-
// Shutdown the express server
|
|
143
|
-
server.close(error => {
|
|
144
|
-
if (error) {
|
|
145
|
-
logger.error(`Express server unable to shutdown correctly, ${error}`);
|
|
146
|
-
} else {
|
|
147
|
-
logger.warn('Express server stopped');
|
|
148
|
-
}
|
|
149
|
-
process.exit(webSocketError || error ? 1 : 0);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
process.on('SIGINT', shutdown);
|
|
155
|
-
process.on('SIGTERM', shutdown);
|
|
156
42
|
}
|
|
157
43
|
|
|
158
44
|
async function startDevMode(config, outputDir, port, extension) {
|
|
159
|
-
const extensionConfig = await
|
|
160
|
-
|
|
45
|
+
const extensionConfig = await getExtensionConfig(config, extension);
|
|
46
|
+
await _buildDevBundle(outputDir, extensionConfig);
|
|
47
|
+
startDevServer(outputDir, port, extensionConfig);
|
|
161
48
|
}
|
|
162
49
|
|
|
163
50
|
module.exports = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/ui-extensions-dev-server",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"tests/testBuild.js",
|
|
24
24
|
"tests/testDevServer.js",
|
|
25
25
|
"plugins/manifestPlugin.js",
|
|
26
|
-
"index.js"
|
|
26
|
+
"index.js",
|
|
27
|
+
"server.js"
|
|
27
28
|
],
|
|
28
29
|
"license": "MIT",
|
|
29
30
|
"dependencies": {
|
|
@@ -31,11 +32,12 @@
|
|
|
31
32
|
"command-line-args": "^5.2.1",
|
|
32
33
|
"command-line-usage": "^7.0.1",
|
|
33
34
|
"console-log-colors": "^0.4.0",
|
|
35
|
+
"cors": "^2.8.5",
|
|
34
36
|
"express": "^4.18.2",
|
|
35
37
|
"process": "^0.11.10",
|
|
36
38
|
"prompts": "^2.4.2",
|
|
37
39
|
"vite": "^4.0.4",
|
|
38
|
-
"ws": "^8.
|
|
40
|
+
"ws": "^8.13.0"
|
|
39
41
|
},
|
|
40
42
|
"bin": {
|
|
41
43
|
"hs-ui-extensions-dev-server": "run.js"
|
|
@@ -56,5 +58,5 @@
|
|
|
56
58
|
"optional": true
|
|
57
59
|
}
|
|
58
60
|
},
|
|
59
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "3b1fd0b35b8aa6c88847d27cc3c6b94e635f0452"
|
|
60
62
|
}
|
|
@@ -12,9 +12,13 @@ function plugin(options = {}) {
|
|
|
12
12
|
name: 'ui-extensions-manifest-generation-plugin',
|
|
13
13
|
enforce: 'post', // run after default rollup plugins
|
|
14
14
|
generateBundle(_rollupOptions, bundle) {
|
|
15
|
-
const {
|
|
15
|
+
const {
|
|
16
|
+
output = DEFAULT_MANIFEST_NAME,
|
|
17
|
+
minify = false,
|
|
18
|
+
extensionConfig,
|
|
19
|
+
} = options;
|
|
16
20
|
try {
|
|
17
|
-
const manifest = _generateManifestContents(bundle);
|
|
21
|
+
const manifest = _generateManifestContents(bundle, extensionConfig);
|
|
18
22
|
this.emitFile({
|
|
19
23
|
type: 'asset',
|
|
20
24
|
source: minify
|
|
@@ -29,9 +33,10 @@ function plugin(options = {}) {
|
|
|
29
33
|
};
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
function _generateManifestContents(bundle) {
|
|
36
|
+
function _generateManifestContents(bundle, extension) {
|
|
33
37
|
const baseManifest = {
|
|
34
38
|
package: _loadPackageFile(),
|
|
39
|
+
extension,
|
|
35
40
|
};
|
|
36
41
|
|
|
37
42
|
// The keys to bundle are the filename without any path information
|
package/server.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const chokidar = require('chokidar');
|
|
3
|
+
const { WebSocketServer } = require('ws');
|
|
4
|
+
const http = require('http');
|
|
5
|
+
const logger = require('./logger');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const cors = require('cors');
|
|
8
|
+
|
|
9
|
+
function startDevServer(outputDir, port, extensionConfig) {
|
|
10
|
+
const callback = `http://hslocal.net:${port}/${extensionConfig?.output}`;
|
|
11
|
+
|
|
12
|
+
const app = express();
|
|
13
|
+
|
|
14
|
+
// Using http.createServer so WebSocket server can be attached before initializing express.
|
|
15
|
+
const server = http.createServer(app);
|
|
16
|
+
|
|
17
|
+
// Setup middleware
|
|
18
|
+
app.use(cors());
|
|
19
|
+
app.use(express.static(outputDir));
|
|
20
|
+
|
|
21
|
+
_addExtensionsEndpoint(app, port);
|
|
22
|
+
|
|
23
|
+
const { broadcast, wss } = _setupWebSocketServer(
|
|
24
|
+
server,
|
|
25
|
+
extensionConfig,
|
|
26
|
+
callback
|
|
27
|
+
);
|
|
28
|
+
_setupChokidarWatcher(broadcast, outputDir);
|
|
29
|
+
|
|
30
|
+
server.listen({ port }, () => {
|
|
31
|
+
logger.warn(`Listening at ${callback}`);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
_configureShutDownHandlers(server, wss, broadcast);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function _setupWebSocketServer(server, extensionConfig, callback) {
|
|
38
|
+
const wss = new WebSocketServer({ server });
|
|
39
|
+
|
|
40
|
+
const baseMessage = {
|
|
41
|
+
appName: extensionConfig?.appName,
|
|
42
|
+
title: extensionConfig?.name,
|
|
43
|
+
callback,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
wss.on('connection', client => {
|
|
47
|
+
logger.info('Browser connected and listening for bundle updates');
|
|
48
|
+
client.send(
|
|
49
|
+
JSON.stringify({
|
|
50
|
+
event: 'start',
|
|
51
|
+
...baseMessage,
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
wss,
|
|
58
|
+
broadcast: message => {
|
|
59
|
+
if (wss.clients.size === 0) {
|
|
60
|
+
logger.warn('No browsers connected to notify');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
wss.clients.forEach(client => {
|
|
64
|
+
client.send(
|
|
65
|
+
JSON.stringify({
|
|
66
|
+
...message,
|
|
67
|
+
...baseMessage,
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Setup a watcher on the dist directory and update broadcast
|
|
76
|
+
//to all clients when an event is observed
|
|
77
|
+
function _setupChokidarWatcher(broadcast, outputDir) {
|
|
78
|
+
chokidar.watch(outputDir).on('all', (event, file) => {
|
|
79
|
+
if (event !== 'change' || event !== 'add' || file === 'manifest.json') {
|
|
80
|
+
// We need to listen to 'change' and 'add' because sometimes chokidar
|
|
81
|
+
// sees it as an 'unlink' and 'add' instead of just a 'change'
|
|
82
|
+
// Since we are adding the manifest.json to the build, we want to ignore changes to that so we don't double send messages
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
logger.info('Bundle updated notifying browser');
|
|
86
|
+
broadcast({
|
|
87
|
+
event: 'update',
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _addExtensionsEndpoint(server, port) {
|
|
93
|
+
const endpoint = '/extensions';
|
|
94
|
+
server.get(endpoint, (_req, res) => {
|
|
95
|
+
try {
|
|
96
|
+
const manifest = require(path.join(process.cwd(), 'dist/manifest.json'));
|
|
97
|
+
const {
|
|
98
|
+
extension: { appName, name, output },
|
|
99
|
+
} = manifest;
|
|
100
|
+
|
|
101
|
+
const response = {
|
|
102
|
+
extensions: [
|
|
103
|
+
{
|
|
104
|
+
appName,
|
|
105
|
+
title: name,
|
|
106
|
+
callback: `http://hslocal.net:${port}/${output}`,
|
|
107
|
+
websocket: `ws://localhost:${port}`,
|
|
108
|
+
manifest: {
|
|
109
|
+
...manifest,
|
|
110
|
+
extension: undefined,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
res.status(200).json(response);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
res.status(500).json({
|
|
118
|
+
message: 'Unable to load manifest file',
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
logger.warn(`Listening at http://hslocal.net:${port}${endpoint}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function _configureShutDownHandlers(server, wss, broadcast) {
|
|
126
|
+
function shutdown() {
|
|
127
|
+
logger.warn('\nCleaning up after ourselves...');
|
|
128
|
+
// Stop new connections to express server
|
|
129
|
+
broadcast({
|
|
130
|
+
event: 'shutdown',
|
|
131
|
+
});
|
|
132
|
+
wss.close(() => {});
|
|
133
|
+
server.close(() => {});
|
|
134
|
+
logger.warn('Clean up done');
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
process.on('SIGINT', shutdown);
|
|
139
|
+
process.on('SIGTERM', shutdown);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = startDevServer;
|
package/tests/runTests.js
CHANGED
package/tests/testDevServer.js
CHANGED
|
@@ -7,11 +7,13 @@ const WebSocket = require('ws');
|
|
|
7
7
|
|
|
8
8
|
const testResults = {
|
|
9
9
|
buildTestPassed: false,
|
|
10
|
-
|
|
10
|
+
expressStaticTestPassed: false,
|
|
11
|
+
extensionsEndpointPassed: false,
|
|
11
12
|
webSocketTestPassed: false,
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
const port = 5172;
|
|
16
|
+
const host = 'hslocal.net';
|
|
15
17
|
|
|
16
18
|
function testDevServer(logger, devServerProcess) {
|
|
17
19
|
logger.warn('\nDev Server Tests started 🤞');
|
|
@@ -29,11 +31,13 @@ function testDevServer(logger, devServerProcess) {
|
|
|
29
31
|
|
|
30
32
|
devServerProcess.stdout.on('data', buffer => {
|
|
31
33
|
const data = buffer.toString().toLowerCase();
|
|
32
|
-
console.log('[Dev Server Log]:', data);
|
|
33
34
|
if (data.includes('built in')) {
|
|
34
35
|
testBuild(testResults, logger);
|
|
35
36
|
}
|
|
36
|
-
if (
|
|
37
|
+
if (
|
|
38
|
+
data.includes('listening') &&
|
|
39
|
+
data.includes(`${host}:${port}/extensions`)
|
|
40
|
+
) {
|
|
37
41
|
setTimeout(() => {
|
|
38
42
|
testExpressServer(testResults, logger);
|
|
39
43
|
testWebSocketServer(testResults, logger);
|
|
@@ -72,10 +76,16 @@ function testDevServer(logger, devServerProcess) {
|
|
|
72
76
|
function metConditions() {
|
|
73
77
|
const {
|
|
74
78
|
buildTestPassed,
|
|
75
|
-
|
|
79
|
+
expressStaticTestPassed,
|
|
80
|
+
extensionsEndpointPassed,
|
|
76
81
|
webSocketTestPassed,
|
|
77
82
|
} = testResults;
|
|
78
|
-
return
|
|
83
|
+
return (
|
|
84
|
+
buildTestPassed &&
|
|
85
|
+
expressStaticTestPassed &&
|
|
86
|
+
extensionsEndpointPassed &&
|
|
87
|
+
webSocketTestPassed
|
|
88
|
+
);
|
|
79
89
|
}
|
|
80
90
|
|
|
81
91
|
// Test that the files were built in the proper location and spot
|
|
@@ -84,7 +94,7 @@ function testBuild(results, logger) {
|
|
|
84
94
|
// // Make sure the files are getting generated in the dist dir
|
|
85
95
|
const distDir = path.join(process.cwd(), 'dist');
|
|
86
96
|
const filesInOutputDir = fs.readdirSync(distDir);
|
|
87
|
-
assert.deepStrictEqual(filesInOutputDir, ['PhoneLines.js']);
|
|
97
|
+
assert.deepStrictEqual(filesInOutputDir, ['PhoneLines.js', 'manifest.json']);
|
|
88
98
|
const fileContents = fs
|
|
89
99
|
.readFileSync(path.join(distDir, filesInOutputDir[0]))
|
|
90
100
|
.toString();
|
|
@@ -110,7 +120,7 @@ function testBuild(results, logger) {
|
|
|
110
120
|
function testExpressServer(results, logger) {
|
|
111
121
|
http.get(
|
|
112
122
|
{
|
|
113
|
-
host
|
|
123
|
+
host,
|
|
114
124
|
port,
|
|
115
125
|
path: '/PhoneLines.js',
|
|
116
126
|
},
|
|
@@ -119,7 +129,36 @@ function testExpressServer(results, logger) {
|
|
|
119
129
|
throw Error('Error with express server');
|
|
120
130
|
}
|
|
121
131
|
logger.info('- Express server connected and serving files 🚀');
|
|
122
|
-
results.
|
|
132
|
+
results.expressStaticTestPassed = true;
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
http.get(
|
|
136
|
+
{
|
|
137
|
+
host,
|
|
138
|
+
port,
|
|
139
|
+
path: '/extensions',
|
|
140
|
+
},
|
|
141
|
+
response => {
|
|
142
|
+
let body = '';
|
|
143
|
+
response.on('data', chunk => {
|
|
144
|
+
body += chunk.toString();
|
|
145
|
+
});
|
|
146
|
+
response.on('end', () => {
|
|
147
|
+
assert(response.statusCode === 200);
|
|
148
|
+
body = JSON.parse(body);
|
|
149
|
+
assert(body.extensions);
|
|
150
|
+
assert.equal(body.extensions.length, 1);
|
|
151
|
+
const extension = body.extensions.pop();
|
|
152
|
+
assert(extension.manifest);
|
|
153
|
+
assert.equal(extension.appName, 'Example App React UI');
|
|
154
|
+
assert.equal(extension.title, 'Phone Lines');
|
|
155
|
+
assert.equal(
|
|
156
|
+
extension.callback,
|
|
157
|
+
`http://${host}:${port}/PhoneLines.js`
|
|
158
|
+
);
|
|
159
|
+
logger.info('- Express serving extension data 🚀');
|
|
160
|
+
results.extensionsEndpointPassed = true;
|
|
161
|
+
});
|
|
123
162
|
}
|
|
124
163
|
);
|
|
125
164
|
}
|
|
@@ -127,20 +166,15 @@ function testExpressServer(results, logger) {
|
|
|
127
166
|
// Test the the web socket server is running on the expected port and
|
|
128
167
|
// that we are able to receive messages from it.
|
|
129
168
|
function testWebSocketServer(results, logger) {
|
|
130
|
-
const fileContents = fs
|
|
131
|
-
.readFileSync(path.join('dist', 'PhoneLines.js'))
|
|
132
|
-
.toString('base64');
|
|
133
|
-
|
|
134
169
|
const ws = new WebSocket(`ws://localhost:${port}`);
|
|
135
170
|
ws.on('message', messageBuffer => {
|
|
136
171
|
const message = JSON.parse(messageBuffer.toString());
|
|
137
|
-
|
|
138
|
-
assert(message.
|
|
139
|
-
assert(message.
|
|
140
|
-
assert.strictEqual(message.extension, 'Phone Lines');
|
|
141
|
-
assert(message.callback, `data:text/javascript;base64,${fileContents}`);
|
|
172
|
+
assert(message.event === 'start');
|
|
173
|
+
assert.strictEqual(message.appName, 'Example App React UI');
|
|
174
|
+
assert.strictEqual(message.title, 'Phone Lines');
|
|
142
175
|
logger.info('- WebSocket server connected and sending messages 🚀');
|
|
143
176
|
results.webSocketTestPassed = true;
|
|
177
|
+
ws.close(); // The test passed, close the connection
|
|
144
178
|
});
|
|
145
179
|
}
|
|
146
180
|
|