@bigbinary/neeto-playwright-commons 3.0.8 → 3.1.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 +1 -0
- package/index.cjs.js +287 -42
- package/index.cjs.js.map +1 -1
- package/index.d.ts +112 -31
- package/index.js +287 -44
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/neeto-chat-widget/setup.sh +15 -0
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { keysToSnakeCase, isPresent, hyphenate, isNotPresent, humanize, dynamicArray, findBy, truncate, isNotEmpty, isNotEqualDeep, randomPick } from '@bigbinary/neeto-cist';
|
|
2
2
|
import { faker } from '@faker-js/faker';
|
|
3
3
|
import * as fs$4 from 'fs';
|
|
4
|
-
import fs__default, { readFileSync, promises, existsSync, writeFileSync, unlinkSync } from 'fs';
|
|
4
|
+
import fs__default, { readFileSync, promises, existsSync, writeFileSync, unlinkSync, mkdirSync, rmSync, createWriteStream } from 'fs';
|
|
5
5
|
import require$$0$4 from 'os';
|
|
6
6
|
import * as path$1 from 'path';
|
|
7
7
|
import path__default from 'path';
|
|
@@ -60508,7 +60508,7 @@ class RailsEmailUtils {
|
|
|
60508
60508
|
});
|
|
60509
60509
|
return otp || undefined;
|
|
60510
60510
|
};
|
|
60511
|
-
getEmailAttachment = async (
|
|
60511
|
+
getEmailAttachment = async ({ name, type }, messageSearchCriteria = {}, { receivedAfter = new Date(new Date().valueOf() - 60 * 60 * 1000), expectedEmailCount = 1, timeout = 10_000, } = {}, shouldThrowErrorOnTimeout = true) => {
|
|
60512
60512
|
const attachmentDetails = (await this.neetoPlaywrightUtilities.executeRecursively({
|
|
60513
60513
|
callback: async () => {
|
|
60514
60514
|
const railsEmail = await this.railsEmailClient.getLatestEmail({
|
|
@@ -60517,7 +60517,11 @@ class RailsEmailUtils {
|
|
|
60517
60517
|
});
|
|
60518
60518
|
if (!railsEmail)
|
|
60519
60519
|
return null;
|
|
60520
|
-
const attachment = railsEmail.attachments?.find(att =>
|
|
60520
|
+
const attachment = railsEmail.attachments?.find(att => {
|
|
60521
|
+
const matchesName = name ? att.name?.includes(name) : true;
|
|
60522
|
+
const matchesType = type ? att.type?.startsWith(type) : true;
|
|
60523
|
+
return matchesName && matchesType;
|
|
60524
|
+
});
|
|
60521
60525
|
if (!attachment)
|
|
60522
60526
|
return null;
|
|
60523
60527
|
return {
|
|
@@ -60701,19 +60705,26 @@ class MailerUtils {
|
|
|
60701
60705
|
return codes?.[0];
|
|
60702
60706
|
};
|
|
60703
60707
|
generateRandomEmail = () => faker.internet.email({ provider: process.env.FASTMAIL_DOMAIN_NAME });
|
|
60704
|
-
getEmailAttachment = async (
|
|
60705
|
-
if (IS_DEV_ENV)
|
|
60706
|
-
return this.railsEmailUtils.getEmailAttachment(
|
|
60707
|
-
}
|
|
60708
|
+
getEmailAttachment = async ({ name, type }, messageSearchCriteria = {}, { timeout = 10_000, receivedAfter = dateTimeOneHourAgo(), expectedEmailCount = 1, } = {}, shouldThrowErrorOnTimeout = true) => {
|
|
60709
|
+
if (IS_DEV_ENV)
|
|
60710
|
+
return this.railsEmailUtils.getEmailAttachment({ name, type }, messageSearchCriteria, { receivedAfter, expectedEmailCount, timeout: timeout / 3 }, shouldThrowErrorOnTimeout);
|
|
60708
60711
|
const { blobId, attachments: attachmentNameAndTypes } = await this.findMessage(messageSearchCriteria, { expectedEmailCount, receivedAfter, timeout }, shouldThrowErrorOnTimeout);
|
|
60709
|
-
const attachment = attachmentNameAndTypes.find(
|
|
60712
|
+
const attachment = attachmentNameAndTypes.find(att => {
|
|
60713
|
+
const matchesName = name ? att.name?.includes(name) : true;
|
|
60714
|
+
const matchesType = type ? att.type?.startsWith(type) : true;
|
|
60715
|
+
return matchesName && matchesType;
|
|
60716
|
+
});
|
|
60710
60717
|
if (!attachment)
|
|
60711
60718
|
throw new Error("No such attachment exists");
|
|
60712
60719
|
const emailAttachment = await this.fastmailApi.fetchAttachments(blobId, attachment.name);
|
|
60713
60720
|
const buffer = await emailAttachment.body();
|
|
60714
60721
|
const parsedEmail = await mailparserExports.simpleParser(buffer);
|
|
60715
60722
|
const attachments = parsedEmail.attachments;
|
|
60716
|
-
return attachments.find(
|
|
60723
|
+
return attachments.find(att => {
|
|
60724
|
+
const matchesName = name ? att.filename?.includes(name) : true;
|
|
60725
|
+
const matchesType = type ? att.contentType?.startsWith(type) : true;
|
|
60726
|
+
return matchesName && matchesType;
|
|
60727
|
+
});
|
|
60717
60728
|
};
|
|
60718
60729
|
}
|
|
60719
60730
|
|
|
@@ -125103,22 +125114,22 @@ async function warmup({ urls = DEFAULT_WARMUP_URLS, timeout = 60_000, } = {}) {
|
|
|
125103
125114
|
}
|
|
125104
125115
|
}
|
|
125105
125116
|
|
|
125106
|
-
const CONFIG = {
|
|
125117
|
+
const CONFIG$1 = {
|
|
125107
125118
|
DIR: "/tmp/neeto-auth-web",
|
|
125108
125119
|
LOG: "/tmp/neeto-auth-server.log",
|
|
125109
125120
|
PORT: 9000,
|
|
125110
125121
|
MAX_WAIT_MS: 120_000,
|
|
125111
125122
|
EMAIL_INIT: "/tmp/neeto-auth-web/config/initializers/playwright_email_capture.rb",
|
|
125112
125123
|
};
|
|
125113
|
-
const SCRIPTS_DIR = path$1.join(__dirname, "scripts", "neeto-auth");
|
|
125124
|
+
const SCRIPTS_DIR$1 = path$1.join(__dirname, "scripts", "neeto-auth");
|
|
125114
125125
|
const escapeRubyString = (value) => value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
125115
125126
|
const buildEmailCaptureInitializer = (targetApp) => {
|
|
125116
|
-
const template = fs$4.readFileSync(path$1.join(SCRIPTS_DIR, "playwright_email_capture.rb.template"), "utf-8");
|
|
125127
|
+
const template = fs$4.readFileSync(path$1.join(SCRIPTS_DIR$1, "playwright_email_capture.rb.template"), "utf-8");
|
|
125117
125128
|
return template.replace("{{TARGET_APP}}", escapeRubyString(targetApp));
|
|
125118
125129
|
};
|
|
125119
|
-
const log = (msg) => console.log(`[NeetoAuth] ${msg}`);
|
|
125120
|
-
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
125121
|
-
const httpCheck = (port, timeout = 3000) => new Promise(resolve => {
|
|
125130
|
+
const log$1 = (msg) => console.log(`[NeetoAuth] ${msg}`);
|
|
125131
|
+
const sleep$1 = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
125132
|
+
const httpCheck$1 = (port, timeout = 3000) => new Promise(resolve => {
|
|
125122
125133
|
const req = http.get(`http://localhost:${port}/`, () => resolve(true));
|
|
125123
125134
|
req.on("error", () => resolve(false));
|
|
125124
125135
|
req.setTimeout(timeout, () => {
|
|
@@ -125145,7 +125156,7 @@ class NeetoAuthServer {
|
|
|
125145
125156
|
while (Date.now() < deadline) {
|
|
125146
125157
|
try {
|
|
125147
125158
|
process.kill(pid, 0);
|
|
125148
|
-
await sleep(500);
|
|
125159
|
+
await sleep$1(500);
|
|
125149
125160
|
}
|
|
125150
125161
|
catch {
|
|
125151
125162
|
return;
|
|
@@ -125154,27 +125165,27 @@ class NeetoAuthServer {
|
|
|
125154
125165
|
};
|
|
125155
125166
|
killServerOnPort = async () => {
|
|
125156
125167
|
try {
|
|
125157
|
-
execSync(`lsof -ti tcp:${CONFIG.PORT} | xargs kill -9`, {
|
|
125168
|
+
execSync(`lsof -ti tcp:${CONFIG$1.PORT} | xargs kill -9`, {
|
|
125158
125169
|
stdio: "ignore",
|
|
125159
125170
|
});
|
|
125160
|
-
await sleep(1000);
|
|
125171
|
+
await sleep$1(1000);
|
|
125161
125172
|
}
|
|
125162
125173
|
catch {
|
|
125163
125174
|
// No process on that port — nothing to kill.
|
|
125164
125175
|
}
|
|
125165
125176
|
};
|
|
125166
125177
|
ensureSetup = () => {
|
|
125167
|
-
if (fs$4.existsSync(`${CONFIG.DIR}/Gemfile`)) {
|
|
125168
|
-
log("Already set up, skipping.");
|
|
125178
|
+
if (fs$4.existsSync(`${CONFIG$1.DIR}/Gemfile`)) {
|
|
125179
|
+
log$1("Already set up, skipping.");
|
|
125169
125180
|
return;
|
|
125170
125181
|
}
|
|
125171
125182
|
if (!process.env.GITHUB_TOKEN) {
|
|
125172
125183
|
throw new Error("[NeetoAuth] GITHUB_TOKEN is not set. Cannot clone neeto-auth-web.");
|
|
125173
125184
|
}
|
|
125174
|
-
log("Running first-time setup...");
|
|
125175
|
-
const setupScript = path$1.join(SCRIPTS_DIR, "setup.sh");
|
|
125185
|
+
log$1("Running first-time setup...");
|
|
125186
|
+
const setupScript = path$1.join(SCRIPTS_DIR$1, "setup.sh");
|
|
125176
125187
|
try {
|
|
125177
|
-
execSync(`bash "${setupScript}" "${CONFIG.DIR}"`, {
|
|
125188
|
+
execSync(`bash "${setupScript}" "${CONFIG$1.DIR}"`, {
|
|
125178
125189
|
stdio: "inherit",
|
|
125179
125190
|
env: this.serverEnv,
|
|
125180
125191
|
});
|
|
@@ -125182,73 +125193,73 @@ class NeetoAuthServer {
|
|
|
125182
125193
|
catch {
|
|
125183
125194
|
throw new Error("[NeetoAuth] First-time setup failed. Check the output above for details.");
|
|
125184
125195
|
}
|
|
125185
|
-
log("Setup complete.");
|
|
125196
|
+
log$1("Setup complete.");
|
|
125186
125197
|
};
|
|
125187
|
-
waitForServer = async (timeoutMs = CONFIG.MAX_WAIT_MS) => {
|
|
125198
|
+
waitForServer = async (timeoutMs = CONFIG$1.MAX_WAIT_MS) => {
|
|
125188
125199
|
const startTime = Date.now();
|
|
125189
125200
|
while (Date.now() - startTime < timeoutMs) {
|
|
125190
125201
|
if (this.process !== null && this.process.exitCode !== null) {
|
|
125191
|
-
const logContent = fs$4.existsSync(CONFIG.LOG)
|
|
125192
|
-
? fs$4.readFileSync(CONFIG.LOG, "utf-8")
|
|
125202
|
+
const logContent = fs$4.existsSync(CONFIG$1.LOG)
|
|
125203
|
+
? fs$4.readFileSync(CONFIG$1.LOG, "utf-8")
|
|
125193
125204
|
: "<no log file>";
|
|
125194
125205
|
throw new Error(`[NeetoAuth] Server process exited unexpectedly with code ${this.process.exitCode}.\nLog:\n${logContent}`);
|
|
125195
125206
|
}
|
|
125196
|
-
if (await httpCheck(CONFIG.PORT))
|
|
125207
|
+
if (await httpCheck$1(CONFIG$1.PORT))
|
|
125197
125208
|
return;
|
|
125198
|
-
await sleep(3000);
|
|
125209
|
+
await sleep$1(3000);
|
|
125199
125210
|
}
|
|
125200
|
-
const logContent = fs$4.existsSync(CONFIG.LOG)
|
|
125201
|
-
? fs$4.readFileSync(CONFIG.LOG, "utf-8")
|
|
125211
|
+
const logContent = fs$4.existsSync(CONFIG$1.LOG)
|
|
125212
|
+
? fs$4.readFileSync(CONFIG$1.LOG, "utf-8")
|
|
125202
125213
|
: "<no log file>";
|
|
125203
125214
|
throw new Error(`[NeetoAuth] Server did not start within ${timeoutMs / 1000}s.\nLog:\n${logContent}`);
|
|
125204
125215
|
};
|
|
125205
125216
|
injectEmailCaptureInitializer = () => {
|
|
125206
|
-
fs$4.writeFileSync(CONFIG.EMAIL_INIT, buildEmailCaptureInitializer(this.targetApp));
|
|
125217
|
+
fs$4.writeFileSync(CONFIG$1.EMAIL_INIT, buildEmailCaptureInitializer(this.targetApp));
|
|
125207
125218
|
};
|
|
125208
125219
|
isRunningForCurrentApp = () => {
|
|
125209
|
-
if (!fs$4.existsSync(CONFIG.EMAIL_INIT))
|
|
125220
|
+
if (!fs$4.existsSync(CONFIG$1.EMAIL_INIT))
|
|
125210
125221
|
return false;
|
|
125211
|
-
const currentContent = fs$4.readFileSync(CONFIG.EMAIL_INIT, "utf-8");
|
|
125222
|
+
const currentContent = fs$4.readFileSync(CONFIG$1.EMAIL_INIT, "utf-8");
|
|
125212
125223
|
return currentContent === buildEmailCaptureInitializer(this.targetApp);
|
|
125213
125224
|
};
|
|
125214
125225
|
start = async () => {
|
|
125215
125226
|
if (IS_STAGING_ENV)
|
|
125216
125227
|
return;
|
|
125217
|
-
if (await httpCheck(CONFIG.PORT)) {
|
|
125228
|
+
if (await httpCheck$1(CONFIG$1.PORT)) {
|
|
125218
125229
|
if (this.isRunningForCurrentApp()) {
|
|
125219
|
-
log("Server already running for this app.");
|
|
125230
|
+
log$1("Server already running for this app.");
|
|
125220
125231
|
return;
|
|
125221
125232
|
}
|
|
125222
|
-
log("Server running for a different app — restarting...");
|
|
125233
|
+
log$1("Server running for a different app — restarting...");
|
|
125223
125234
|
await this.killServerOnPort();
|
|
125224
125235
|
}
|
|
125225
125236
|
this.ensureSetup();
|
|
125226
125237
|
this.injectEmailCaptureInitializer();
|
|
125227
|
-
log("Starting server...");
|
|
125228
|
-
const logStream = fs$4.createWriteStream(CONFIG.LOG);
|
|
125238
|
+
log$1("Starting server...");
|
|
125239
|
+
const logStream = fs$4.createWriteStream(CONFIG$1.LOG);
|
|
125229
125240
|
this.process = spawn("bundle", [
|
|
125230
125241
|
"exec",
|
|
125231
125242
|
"rails",
|
|
125232
125243
|
"server",
|
|
125233
125244
|
"-p",
|
|
125234
|
-
CONFIG.PORT.toString(),
|
|
125245
|
+
CONFIG$1.PORT.toString(),
|
|
125235
125246
|
"-b",
|
|
125236
125247
|
"0.0.0.0",
|
|
125237
125248
|
], {
|
|
125238
|
-
cwd: CONFIG.DIR,
|
|
125249
|
+
cwd: CONFIG$1.DIR,
|
|
125239
125250
|
stdio: ["ignore", "pipe", "pipe"],
|
|
125240
125251
|
env: this.serverEnv,
|
|
125241
125252
|
});
|
|
125242
125253
|
this.process.stdout?.pipe(logStream);
|
|
125243
125254
|
this.process.stderr?.pipe(logStream);
|
|
125244
125255
|
await this.waitForServer();
|
|
125245
|
-
log("Server ready.");
|
|
125256
|
+
log$1("Server ready.");
|
|
125246
125257
|
};
|
|
125247
125258
|
stop = async () => {
|
|
125248
125259
|
if (IS_STAGING_ENV || !this.process)
|
|
125249
125260
|
return;
|
|
125250
125261
|
const pid = this.process.pid;
|
|
125251
|
-
log("Stopping server...");
|
|
125262
|
+
log$1("Stopping server...");
|
|
125252
125263
|
this.process.kill("SIGTERM");
|
|
125253
125264
|
this.process = null;
|
|
125254
125265
|
if (pid) {
|
|
@@ -125257,6 +125268,238 @@ class NeetoAuthServer {
|
|
|
125257
125268
|
};
|
|
125258
125269
|
}
|
|
125259
125270
|
|
|
125271
|
+
const CONFIG = {
|
|
125272
|
+
DIR: "/tmp/neeto-chat-widget",
|
|
125273
|
+
LOG: "/tmp/neeto-chat-widget.log",
|
|
125274
|
+
PORT: 8081,
|
|
125275
|
+
MAX_WAIT_MS: 180_000,
|
|
125276
|
+
LOCK_RETRY_MS: 500,
|
|
125277
|
+
SERVER_POLL_INTERVAL_MS: 3000,
|
|
125278
|
+
WEBPACK_DEV_FILE: "/tmp/neeto-chat-widget/webpack.dev.js",
|
|
125279
|
+
LOCK_DIR: "/tmp/neeto-chat-widget.lock",
|
|
125280
|
+
STATE_FILE: "/tmp/neeto-chat-widget.state.json",
|
|
125281
|
+
};
|
|
125282
|
+
const DEFAULT_STATE = { pid: null, refCount: 0 };
|
|
125283
|
+
const WIDGET_BASE_URL = `http://localhost:${CONFIG.PORT}`;
|
|
125284
|
+
const SCRIPTS_DIR = path$1.join(__dirname, "scripts", "neeto-chat-widget");
|
|
125285
|
+
const log = (msg) => console.log(`[NeetoChatWidget] ${msg}`);
|
|
125286
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
125287
|
+
const httpCheck = (timeout = 3000) => new Promise(resolve => {
|
|
125288
|
+
const req = http.get(`${WIDGET_BASE_URL}/main.js`, () => resolve(true));
|
|
125289
|
+
req.on("error", () => resolve(false));
|
|
125290
|
+
req.setTimeout(timeout, () => {
|
|
125291
|
+
req.destroy();
|
|
125292
|
+
resolve(false);
|
|
125293
|
+
});
|
|
125294
|
+
});
|
|
125295
|
+
class NeetoChatWidget {
|
|
125296
|
+
process = null;
|
|
125297
|
+
hasLease = false;
|
|
125298
|
+
logStream = null;
|
|
125299
|
+
get serverEnv() {
|
|
125300
|
+
return { ...process.env };
|
|
125301
|
+
}
|
|
125302
|
+
withLock = async (fn) => {
|
|
125303
|
+
while (true) {
|
|
125304
|
+
try {
|
|
125305
|
+
mkdirSync(CONFIG.LOCK_DIR);
|
|
125306
|
+
break;
|
|
125307
|
+
}
|
|
125308
|
+
catch (error) {
|
|
125309
|
+
const nodeError = error;
|
|
125310
|
+
if (nodeError.code !== "EEXIST")
|
|
125311
|
+
throw error;
|
|
125312
|
+
await sleep(CONFIG.LOCK_RETRY_MS);
|
|
125313
|
+
}
|
|
125314
|
+
}
|
|
125315
|
+
try {
|
|
125316
|
+
return await Promise.resolve(fn());
|
|
125317
|
+
}
|
|
125318
|
+
finally {
|
|
125319
|
+
rmSync(CONFIG.LOCK_DIR, { force: true, recursive: true });
|
|
125320
|
+
}
|
|
125321
|
+
};
|
|
125322
|
+
parseState = (raw) => {
|
|
125323
|
+
if (!raw || typeof raw !== "object")
|
|
125324
|
+
return DEFAULT_STATE;
|
|
125325
|
+
const state = raw;
|
|
125326
|
+
const pid = typeof state.pid === "number" ? state.pid : null;
|
|
125327
|
+
const refCount = typeof state.refCount === "number" && state.refCount > 0
|
|
125328
|
+
? state.refCount
|
|
125329
|
+
: 0;
|
|
125330
|
+
return { pid, refCount };
|
|
125331
|
+
};
|
|
125332
|
+
readState = () => {
|
|
125333
|
+
if (!existsSync(CONFIG.STATE_FILE))
|
|
125334
|
+
return DEFAULT_STATE;
|
|
125335
|
+
try {
|
|
125336
|
+
return this.parseState(JSON.parse(readFileSync(CONFIG.STATE_FILE, "utf-8")));
|
|
125337
|
+
}
|
|
125338
|
+
catch {
|
|
125339
|
+
return DEFAULT_STATE;
|
|
125340
|
+
}
|
|
125341
|
+
};
|
|
125342
|
+
writeState = (state) => {
|
|
125343
|
+
writeFileSync(CONFIG.STATE_FILE, JSON.stringify(state));
|
|
125344
|
+
};
|
|
125345
|
+
clearState = () => {
|
|
125346
|
+
existsSync(CONFIG.STATE_FILE) && rmSync(CONFIG.STATE_FILE, { force: true });
|
|
125347
|
+
};
|
|
125348
|
+
getLogContent = () => existsSync(CONFIG.LOG)
|
|
125349
|
+
? readFileSync(CONFIG.LOG, "utf-8")
|
|
125350
|
+
: "<no log file>";
|
|
125351
|
+
ensureSetup = () => {
|
|
125352
|
+
if (existsSync(CONFIG.WEBPACK_DEV_FILE)) {
|
|
125353
|
+
this.patchWebpackPublicPath();
|
|
125354
|
+
log("Already set up, skipping.");
|
|
125355
|
+
return;
|
|
125356
|
+
}
|
|
125357
|
+
existsSync(CONFIG.DIR) &&
|
|
125358
|
+
rmSync(CONFIG.DIR, { force: true, recursive: true });
|
|
125359
|
+
const setupScript = path$1.join(SCRIPTS_DIR, "setup.sh");
|
|
125360
|
+
log("Running first-time setup...");
|
|
125361
|
+
try {
|
|
125362
|
+
execSync(`bash "${setupScript}" "${CONFIG.DIR}"`, {
|
|
125363
|
+
stdio: "inherit",
|
|
125364
|
+
env: this.serverEnv,
|
|
125365
|
+
});
|
|
125366
|
+
}
|
|
125367
|
+
catch {
|
|
125368
|
+
throw new Error("[NeetoChatWidget] First-time setup failed. Check the output above for details.");
|
|
125369
|
+
}
|
|
125370
|
+
this.patchWebpackPublicPath();
|
|
125371
|
+
log("Setup complete.");
|
|
125372
|
+
};
|
|
125373
|
+
patchWebpackPublicPath = () => {
|
|
125374
|
+
if (!existsSync(CONFIG.WEBPACK_DEV_FILE)) {
|
|
125375
|
+
throw new Error(`[NeetoChatWidget] Missing webpack config at ${CONFIG.WEBPACK_DEV_FILE}`);
|
|
125376
|
+
}
|
|
125377
|
+
const fileContent = readFileSync(CONFIG.WEBPACK_DEV_FILE, "utf-8");
|
|
125378
|
+
const targetPublicPath = `publicPath: "${WIDGET_BASE_URL}/"`;
|
|
125379
|
+
if (fileContent.includes(targetPublicPath))
|
|
125380
|
+
return;
|
|
125381
|
+
const oldConfig = ` devServer: {\n contentBase: "./src",\n allowedHosts: [".lvh.me"],\n },\n`;
|
|
125382
|
+
const newConfig = ` devServer: {\n contentBase: "./src",\n allowedHosts: [".lvh.me"],\n publicPath: "${WIDGET_BASE_URL}/",\n },\n output: {\n publicPath: "${WIDGET_BASE_URL}/",\n },\n`;
|
|
125383
|
+
if (!fileContent.includes(oldConfig)) {
|
|
125384
|
+
throw new Error("[NeetoChatWidget] Unable to patch webpack.dev.js for local widget serving.");
|
|
125385
|
+
}
|
|
125386
|
+
writeFileSync(CONFIG.WEBPACK_DEV_FILE, fileContent.replace(oldConfig, newConfig));
|
|
125387
|
+
};
|
|
125388
|
+
closeLogStream = () => {
|
|
125389
|
+
if (this.logStream) {
|
|
125390
|
+
this.logStream.end();
|
|
125391
|
+
this.logStream = null;
|
|
125392
|
+
}
|
|
125393
|
+
};
|
|
125394
|
+
waitForProcessExit = async (pid) => {
|
|
125395
|
+
const deadline = Date.now() + 10_000;
|
|
125396
|
+
while (Date.now() < deadline) {
|
|
125397
|
+
try {
|
|
125398
|
+
process.kill(pid, 0);
|
|
125399
|
+
await sleep(500);
|
|
125400
|
+
}
|
|
125401
|
+
catch {
|
|
125402
|
+
return;
|
|
125403
|
+
}
|
|
125404
|
+
}
|
|
125405
|
+
};
|
|
125406
|
+
killServer = (pid) => {
|
|
125407
|
+
if (!pid)
|
|
125408
|
+
return;
|
|
125409
|
+
try {
|
|
125410
|
+
process.kill(pid, "SIGTERM");
|
|
125411
|
+
}
|
|
125412
|
+
catch {
|
|
125413
|
+
// process may already be gone
|
|
125414
|
+
}
|
|
125415
|
+
};
|
|
125416
|
+
killProcessOnWidgetPort = async () => {
|
|
125417
|
+
try {
|
|
125418
|
+
execSync(`lsof -ti tcp:${CONFIG.PORT} | xargs kill -9`, {
|
|
125419
|
+
stdio: "ignore",
|
|
125420
|
+
});
|
|
125421
|
+
await sleep(1000);
|
|
125422
|
+
}
|
|
125423
|
+
catch {
|
|
125424
|
+
// No process on that port — nothing to kill.
|
|
125425
|
+
}
|
|
125426
|
+
};
|
|
125427
|
+
waitForServer = async (timeoutMs = CONFIG.MAX_WAIT_MS) => {
|
|
125428
|
+
const startTime = Date.now();
|
|
125429
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
125430
|
+
if (this.process !== null && this.process.exitCode !== null) {
|
|
125431
|
+
throw new Error(`[NeetoChatWidget] Server process exited unexpectedly with code ${this.process.exitCode}.\nLog:\n${this.getLogContent()}`);
|
|
125432
|
+
}
|
|
125433
|
+
if (await httpCheck())
|
|
125434
|
+
return;
|
|
125435
|
+
await sleep(CONFIG.SERVER_POLL_INTERVAL_MS);
|
|
125436
|
+
}
|
|
125437
|
+
throw new Error(`[NeetoChatWidget] Widget did not start within ${timeoutMs / 1000}s.\nLog:\n${this.getLogContent()}`);
|
|
125438
|
+
};
|
|
125439
|
+
start = async () => {
|
|
125440
|
+
if (IS_STAGING_ENV)
|
|
125441
|
+
return;
|
|
125442
|
+
await this.withLock(async () => {
|
|
125443
|
+
const state = this.readState();
|
|
125444
|
+
if (await httpCheck()) {
|
|
125445
|
+
this.hasLease = true;
|
|
125446
|
+
this.writeState({ ...state, refCount: state.refCount + 1 });
|
|
125447
|
+
log("Server already running.");
|
|
125448
|
+
return;
|
|
125449
|
+
}
|
|
125450
|
+
this.ensureSetup();
|
|
125451
|
+
log("Starting server...");
|
|
125452
|
+
this.logStream = createWriteStream(CONFIG.LOG);
|
|
125453
|
+
this.process = spawn("yarn", ["start"], {
|
|
125454
|
+
cwd: CONFIG.DIR,
|
|
125455
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
125456
|
+
});
|
|
125457
|
+
this.process.stdout?.pipe(this.logStream);
|
|
125458
|
+
this.process.stderr?.pipe(this.logStream);
|
|
125459
|
+
try {
|
|
125460
|
+
await this.waitForServer();
|
|
125461
|
+
}
|
|
125462
|
+
catch (error) {
|
|
125463
|
+
const pid = this.process?.pid;
|
|
125464
|
+
this.killServer(pid);
|
|
125465
|
+
pid && (await this.waitForProcessExit(pid));
|
|
125466
|
+
(await httpCheck()) && (await this.killProcessOnWidgetPort());
|
|
125467
|
+
this.process = null;
|
|
125468
|
+
this.closeLogStream();
|
|
125469
|
+
throw error;
|
|
125470
|
+
}
|
|
125471
|
+
this.hasLease = true;
|
|
125472
|
+
this.writeState({
|
|
125473
|
+
pid: this.process.pid ?? null,
|
|
125474
|
+
refCount: state.refCount + 1,
|
|
125475
|
+
});
|
|
125476
|
+
log("Server ready.");
|
|
125477
|
+
});
|
|
125478
|
+
};
|
|
125479
|
+
stop = async () => {
|
|
125480
|
+
if (IS_STAGING_ENV || !this.hasLease)
|
|
125481
|
+
return;
|
|
125482
|
+
await this.withLock(async () => {
|
|
125483
|
+
const state = this.readState();
|
|
125484
|
+
const nextRefCount = Math.max(state.refCount - 1, 0);
|
|
125485
|
+
this.hasLease = false;
|
|
125486
|
+
if (nextRefCount > 0) {
|
|
125487
|
+
this.writeState({ ...state, refCount: nextRefCount });
|
|
125488
|
+
return;
|
|
125489
|
+
}
|
|
125490
|
+
if (state.pid) {
|
|
125491
|
+
log("Stopping server...");
|
|
125492
|
+
this.killServer(state.pid);
|
|
125493
|
+
await this.waitForProcessExit(state.pid);
|
|
125494
|
+
(await httpCheck()) && (await this.killProcessOnWidgetPort());
|
|
125495
|
+
}
|
|
125496
|
+
this.process = null;
|
|
125497
|
+
this.closeLogStream();
|
|
125498
|
+
this.clearState();
|
|
125499
|
+
});
|
|
125500
|
+
};
|
|
125501
|
+
}
|
|
125502
|
+
|
|
125260
125503
|
const NEETO_EMAIL_DELIVERY_TESTING_BASE_URL = "/neeto_email_delivery/api/v1/testing";
|
|
125261
125504
|
class NeetoEmailDeliveryApi {
|
|
125262
125505
|
neetoPlaywrightUtilities;
|
|
@@ -126179,5 +126422,5 @@ const definePlaywrightConfig = (overrides) => {
|
|
|
126179
126422
|
});
|
|
126180
126423
|
};
|
|
126181
126424
|
|
|
126182
|
-
export { ACTIONS, ADMIN_PANEL_SELECTORS, ALL_RESOURCES, ANALYTICS_RESOURCES, API_KEYS_SELECTORS, API_ROUTES, APP_RESOURCES, AUDIT_LOGS_SELECTORS, ApiKeysApi, ApiKeysPage, AuditLogsPage, BASE_URL, CALENDAR_LABELS, CERTIFICATE_LIMIT_EXCEEDED_MESSAGE, CERTIFICATE_LIMIT_EXCEEDED_REGEXP, CHANGELOG_WIDGET_SELECTORS, CHAT_WIDGET_SELECTORS, CHAT_WIDGET_TEXTS, COLOR, COMMON_SELECTORS, COMMON_TEXTS, COMMUNITY_TEXTS, CREDENTIALS, CURRENT_TIME_RANGES, CUSTOM_DOMAIN_SELECTORS, CUSTOM_DOMAIN_SUFFIX, CustomCommands, CustomDomainApi, CustomDomainPage, DATE_FORMATS, DATE_PICKER_SELECTORS, DATE_RANGES, DATE_TEXTS, DEFAULT_WEBHOOKS_RESPONSE_TEXT, DESCRIPTION_EDITOR_TEXTS, EMBED_SELECTORS, EMOJI_LABEL, EMPTY_STORAGE_STATE, ENGAGE_TEXTS, ENVIRONMENT, EXAMPLE_URL, EXPANDED_FONT_SIZE, EXPORT_FILE_TYPES, EditorPage, EmailDeliveryUtils, EmbedBase, FILE_FORMATS, FONTS_RESOURCES, FONT_SIZE_SELECTORS, FROM_EMAIL_ENV_KEYS, GLOBAL_TRANSLATIONS_PATTERN, GOOGLE_ANALYTICS_SELECTORS, GOOGLE_CALENDAR_DATE_FORMAT, GOOGLE_LOGIN_SELECTORS, GOOGLE_LOGIN_TEXTS, GOOGLE_SHEETS_SELECTORS, GooglePage, HELP_CENTER_ROUTES, HELP_CENTER_SELECTORS, HelpAndProfilePage, INTEGRATIONS_TEXTS, INTEGRATION_SELECTORS, IPRestrictionsPage, IP_RESTRICTIONS_SELECTORS, IS_CI, IS_DEV_ENV, IS_STAGING_ENV, ImageUploader, IntegrationBase, IpRestrictionsApi, KEYBOARD_SHORTCUTS_SELECTORS, KEYBOARD_SHORTCUT_TEST_CASES, LIST_MODIFIER_SELECTORS, LIST_MODIFIER_TAGS, LOGIN_SELECTORS, MEMBER_FORM_SELECTORS, MEMBER_SELECTORS, MEMBER_TEXTS, MERGE_TAGS_SELECTORS, MICROSOFT_LOGIN_SELECTORS, MICROSOFT_LOGIN_TEXTS, MailerUtils, Member, MemberApis, MicrosoftPage, NEETO_AUTH_BASE_URL, NEETO_EDITOR_SELECTORS, NEETO_FILTERS_SELECTORS, NEETO_IMAGE_UPLOADER_SELECTORS, NEETO_ROUTES, NEETO_SEO_SELECTORS, NEETO_TEXT_MODIFIER_SELECTORS, NeetoAuthServer, NeetoEmailDeliveryApi, NeetoTowerApi, ONBOARDING_SELECTORS, ORGANIZATION_TEXTS, OTP_EMAIL_PATTERN, OrganizationPage, PAST_TIME_RANGES, PHONE_NUMBER_FORMATS, PLURAL, PROFILE_LINKS, PROFILE_SECTION_SELECTORS, PROJECT_NAMES, PROJECT_TRANSLATIONS_PATH, ROLES_SELECTORS, ROUTES, RailsEmailApiClient, RailsEmailUtils, RoleApis, RolesPage, SIGNUP_SELECTORS, SINGULAR, SLACK_DATA_QA_SELECTORS, SLACK_DEFAULT_CHANNEL, SLACK_SELECTORS, SLACK_WEB_TEXTS, STATUS_TEXTS, STORAGE_STATE, SecurityApi, SidebarSection, SlackApi, SlackPage, TABLE_SELECTORS, TAB_SELECTORS, TAGS_SELECTORS, TEAM_MEMBER_TEXTS, TEXT_MODIFIER_ROLES, TEXT_MODIFIER_SELECTORS, TEXT_MODIFIER_TAGS, THANK_YOU_SELECTORS, THEMES_SELECTORS, THEMES_TEXTS, THIRD_PARTY_RESOURCES, THIRD_PARTY_ROUTES, TIME_RANGES, TOASTR_MESSAGES, TWILIO_SELECTORS, TagsApi, TagsPage, TeamMembers, ThankYouApi, ThankYouPage, TwilioApi, USER_AGENTS, WEBHOOK_SELECTORS, WebhookSiteApi, WebhooksPage, ZAPIER_LIMIT_EXHAUSTED_MESSAGE, ZAPIER_SELECTORS, ZAPIER_TEST_EMAIL, ZAPIER_WEB_TEXTS, ZapierPage, baseURLGenerator, basicHTMLContent, clearCredentials, commands, cpuThrottlingUsingCDP, createOrganizationViaRake, currencyUtils, dataQa, decodeQRCodeFromFile, definePlaywrightConfig, executeWithThrottledResources, extractSubdomainFromError, fillCredentialsAndSubmit, filterUtils, generatePhoneNumber, generatePhoneNumberDetails, generateRandomBypassEmail, generateRandomFile, generateStagingData, getByDataQA, getClipboardContent, getFormattedPhoneNumber, getFullUrl, getGlobalUserProps, getGlobalUserState, getImagePathAndName, getIsoCodeFromPhoneCode, getListCount, globalShortcuts, grantClipboardPermissions, hexToRGB, hexToRGBA, i18nFixture, imageRegex, initializeCredentials, initializeTestData, initializeTotp, isGithubIssueOpen, joinHyphenCase, joinString, login, loginWithoutSSO, networkConditions, networkThrottlingUsingCDP, readFileSyncIfExists, removeCredentialFile, serializeFileForBrowser, shouldSkipCustomDomainSetup, shouldSkipSetupAndTeardown, simulateClickWithDelay, simulateTypingWithDelay, skipTest, squish, stealth as stealthTest, tableUtils, toCamelCase, updateCredentials, warmup, writeDataToFile };
|
|
126425
|
+
export { ACTIONS, ADMIN_PANEL_SELECTORS, ALL_RESOURCES, ANALYTICS_RESOURCES, API_KEYS_SELECTORS, API_ROUTES, APP_RESOURCES, AUDIT_LOGS_SELECTORS, ApiKeysApi, ApiKeysPage, AuditLogsPage, BASE_URL, CALENDAR_LABELS, CERTIFICATE_LIMIT_EXCEEDED_MESSAGE, CERTIFICATE_LIMIT_EXCEEDED_REGEXP, CHANGELOG_WIDGET_SELECTORS, CHAT_WIDGET_SELECTORS, CHAT_WIDGET_TEXTS, COLOR, COMMON_SELECTORS, COMMON_TEXTS, COMMUNITY_TEXTS, CREDENTIALS, CURRENT_TIME_RANGES, CUSTOM_DOMAIN_SELECTORS, CUSTOM_DOMAIN_SUFFIX, ColorPickerUtils, CustomCommands, CustomDomainApi, CustomDomainPage, DATE_FORMATS, DATE_PICKER_SELECTORS, DATE_RANGES, DATE_TEXTS, DEFAULT_WEBHOOKS_RESPONSE_TEXT, DESCRIPTION_EDITOR_TEXTS, EMBED_SELECTORS, EMOJI_LABEL, EMPTY_STORAGE_STATE, ENGAGE_TEXTS, ENVIRONMENT, EXAMPLE_URL, EXPANDED_FONT_SIZE, EXPORT_FILE_TYPES, EditorPage, EmailDeliveryUtils, EmbedBase, FILE_FORMATS, FONTS_RESOURCES, FONT_SIZE_SELECTORS, FROM_EMAIL_ENV_KEYS, GLOBAL_TRANSLATIONS_PATTERN, GOOGLE_ANALYTICS_SELECTORS, GOOGLE_CALENDAR_DATE_FORMAT, GOOGLE_LOGIN_SELECTORS, GOOGLE_LOGIN_TEXTS, GOOGLE_SHEETS_SELECTORS, GooglePage, HELP_CENTER_ROUTES, HELP_CENTER_SELECTORS, HelpAndProfilePage, INTEGRATIONS_TEXTS, INTEGRATION_SELECTORS, IPRestrictionsPage, IP_RESTRICTIONS_SELECTORS, IS_CI, IS_DEV_ENV, IS_STAGING_ENV, ImageUploader, IntegrationBase, IpRestrictionsApi, KEYBOARD_SHORTCUTS_SELECTORS, KEYBOARD_SHORTCUT_TEST_CASES, LIST_MODIFIER_SELECTORS, LIST_MODIFIER_TAGS, LOGIN_SELECTORS, MEMBER_FORM_SELECTORS, MEMBER_SELECTORS, MEMBER_TEXTS, MERGE_TAGS_SELECTORS, MICROSOFT_LOGIN_SELECTORS, MICROSOFT_LOGIN_TEXTS, MailerUtils, Member, MemberApis, MicrosoftPage, NEETO_AUTH_BASE_URL, NEETO_EDITOR_SELECTORS, NEETO_FILTERS_SELECTORS, NEETO_IMAGE_UPLOADER_SELECTORS, NEETO_ROUTES, NEETO_SEO_SELECTORS, NEETO_TEXT_MODIFIER_SELECTORS, NeetoAuthServer, NeetoChatWidget, NeetoEmailDeliveryApi, NeetoTowerApi, ONBOARDING_SELECTORS, ORGANIZATION_TEXTS, OTP_EMAIL_PATTERN, OrganizationPage, PAST_TIME_RANGES, PHONE_NUMBER_FORMATS, PLURAL, PROFILE_LINKS, PROFILE_SECTION_SELECTORS, PROJECT_NAMES, PROJECT_TRANSLATIONS_PATH, ROLES_SELECTORS, ROUTES, RailsEmailApiClient, RailsEmailUtils, RoleApis, RolesPage, SIGNUP_SELECTORS, SINGULAR, SLACK_DATA_QA_SELECTORS, SLACK_DEFAULT_CHANNEL, SLACK_SELECTORS, SLACK_WEB_TEXTS, STATUS_TEXTS, STORAGE_STATE, SecurityApi, SidebarSection, SlackApi, SlackPage, TABLE_SELECTORS, TAB_SELECTORS, TAGS_SELECTORS, TEAM_MEMBER_TEXTS, TEXT_MODIFIER_ROLES, TEXT_MODIFIER_SELECTORS, TEXT_MODIFIER_TAGS, THANK_YOU_SELECTORS, THEMES_SELECTORS, THEMES_TEXTS, THIRD_PARTY_RESOURCES, THIRD_PARTY_ROUTES, TIME_RANGES, TOASTR_MESSAGES, TWILIO_SELECTORS, TagsApi, TagsPage, TeamMembers, ThankYouApi, ThankYouPage, TwilioApi, USER_AGENTS, WEBHOOK_SELECTORS, WebhookSiteApi, WebhooksPage, ZAPIER_LIMIT_EXHAUSTED_MESSAGE, ZAPIER_SELECTORS, ZAPIER_TEST_EMAIL, ZAPIER_WEB_TEXTS, ZapierPage, baseURLGenerator, basicHTMLContent, clearCredentials, commands, cpuThrottlingUsingCDP, createOrganizationViaRake, currencyUtils, dataQa, decodeQRCodeFromFile, definePlaywrightConfig, executeWithThrottledResources, extractSubdomainFromError, fillCredentialsAndSubmit, filterUtils, generatePhoneNumber, generatePhoneNumberDetails, generateRandomBypassEmail, generateRandomFile, generateStagingData, getByDataQA, getClipboardContent, getFormattedPhoneNumber, getFullUrl, getGlobalUserProps, getGlobalUserState, getImagePathAndName, getIsoCodeFromPhoneCode, getListCount, globalShortcuts, grantClipboardPermissions, hexToRGB, hexToRGBA, i18nFixture, imageRegex, initializeCredentials, initializeTestData, initializeTotp, isGithubIssueOpen, joinHyphenCase, joinString, login, loginWithoutSSO, networkConditions, networkThrottlingUsingCDP, readFileSyncIfExists, removeCredentialFile, serializeFileForBrowser, shouldSkipCustomDomainSetup, shouldSkipSetupAndTeardown, simulateClickWithDelay, simulateTypingWithDelay, skipTest, squish, stealth as stealthTest, tableUtils, toCamelCase, updateCredentials, warmup, writeDataToFile };
|
|
126183
126426
|
//# sourceMappingURL=index.js.map
|