2021-10-30 10:37:32 +03:00
|
|
|
const { spawnSync, spawn } = require("child_process");
|
|
|
|
const fs = require("fs");
|
|
|
|
const path = require("path");
|
|
|
|
const _ = require("lodash");
|
|
|
|
const commander = require("commander");
|
2022-06-24 07:51:07 +03:00
|
|
|
const { exit } = require("process");
|
2021-10-30 10:37:32 +03:00
|
|
|
|
2022-09-20 08:36:03 +03:00
|
|
|
const nxPath = path.join(__dirname, "../node_modules/@nrwl/cli/bin/nx.js");
|
2021-12-07 04:53:39 +03:00
|
|
|
const baseDir = path.join(__dirname, "../notebooks/responsibleaidashboard");
|
2023-08-17 20:13:35 +03:00
|
|
|
const tabularDir = path.join(baseDir, "tabular");
|
|
|
|
const visionDir = path.join(baseDir, "vision");
|
2023-08-23 22:53:13 +03:00
|
|
|
const textDir = path.join(baseDir, "text");
|
2022-01-07 23:24:24 +03:00
|
|
|
const filePrefix = "responsibleaidashboard-";
|
2023-08-17 20:13:35 +03:00
|
|
|
// Please add notebook name into the appropriate 'fileNames' array only when you are adding e2e tests to that notebook.
|
2023-07-13 22:38:49 +03:00
|
|
|
// Keep this list in sync with .github/workflows/CI-e2e-notebooks.yml and/or .github/workflows/CI-e2e-notebooks-vision.yml
|
2023-08-17 20:13:35 +03:00
|
|
|
const tabularFileNames = [
|
2022-01-07 23:24:24 +03:00
|
|
|
"responsibleaidashboard-census-classification-model-debugging",
|
2022-01-13 23:56:55 +03:00
|
|
|
"responsibleaidashboard-diabetes-regression-model-debugging",
|
2022-01-14 23:47:41 +03:00
|
|
|
"responsibleaidashboard-housing-classification-model-debugging",
|
2022-02-10 02:00:48 +03:00
|
|
|
"responsibleaidashboard-diabetes-decision-making",
|
|
|
|
"responsibleaidashboard-housing-decision-making",
|
Add forecasting to raiwidgets and add a forecasting notebook (#2252)
* Add postga build trigger (#1755) (#1756)
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
* Add model wrapper for wrapping predictions and test data (#1762)
* Add model wrapper for wrapping predictions and test data
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
* Fix failing tests
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
* Change description of cohort selection panel in Aggregate Feature Importance (#1770)
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
* Support cohort filtering of string target in rai_insights (#1771)
* Port tests
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
* Fix rai_insights
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
* Simplify tests in test_cohort_filter.py (#1772)
* Simply tests in test_cohort_filter.py
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
* Change test name
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
* Forecasting Dashboard
This commit adds the Forecasting Dashboard capabilities in both UI and SDK.
* remove unrelated file
* basic set of changes to get it working again
* forecasting_grains -> time_series_id_column_names
* fix breaking changes in latest main, add quantile prediction, fix date conversion, remove obsolete managers
* fix UI components (including table and what-if creation, chart still left tbd)
* get dashboard to work with multiple time series and switching back and forth
* remove data explorer and model overview, create and edit cohort functionalities from forecasting
* lintfix
* localizzation
* remove console.log
* remove question
* remove unrelated changes, fix package.json
* string fixes and package.json adjustment
* localization
* localization
* more localization and removing of unrelated files
* remove unrelated files
* remove unrelated file changes
* remove is_forecasting_true_y
* lintfix
* add dropdown label
* feature metadata input validation updates
* cache transformation predictions
* lintfix
* remove isUndefinedOrEmpty
* remove isUndefinedOrEmpty
* rename isAllDataCohort file as requested
* lintfix
* first draft of notebook
* get notebook to run, generate cohorts (not finished)
* ntoebook
* convert filter args and column names properly
* fix datetime handling
* fix forecast request handling
* working version for bug bash
* add wheels
* add UI changes
* update notebook to fix model download
* 15 iterations
* lintfix
* add example of operation method string
* add check for context
* add explanatory comment
* move description creation logic into getDescription
* lintfix
* remove datetime_features from snapshot
* lintfix and other weirdnesses
* undo print removal
* remove wheels, lintfix, small cleanup
* use META_JSON directly instead of declaring a variable
* move constants to common files and reuse
* lowercase f
* isort
* remove datetime_features
* sktime notebook (1st half)
* isort
* debugging the notebook issue
* use parentheses instead of backslashes
* flake8
* isort
* sktime progress
* pre-unified RAIInsights commit
* pre-unified RAIInsights commit 2
* first pass over unification
* unify RAIInsights and RAIForecastingInsights
* adjustments to serialization
* cleanup, test fixes, consolidation, TODO fixes
* fix tests
* isort
* flake8
* feature metadata and categorical_features consolidation
* fix serialization of feature metadata
* fix tests
* flake8
* tests for feature metadata and cat features
* flake8
* remove remnants of RAIForecastingInsights
* fix string
* move other utils back to rai insights
* undo ModelTask import changes
* undo ModelTask references to raiutils
* undo references to ModelTask in raituils
* remove raiutils changes since they're in a separate branch
* datetime_features and time_series_id_features replacement
* pytest.mark.skip
* furthe datetime_features fixes
* TS ID col name fix
* try out asfreq
* notebook try asfreq interpolation
* attempt to condense index into a single col
* add hidden feature flag, fix tests
* remove experimental file, isort
* undo string change
* restructure wrappers and field to file mapping code
* remove unnecessary TODO
* get AML notebook to work
* get AML notebook fully working
* resolve some issues with sktime
* notebook
* remove duplicated test case
* flake8
* isort
* flalke8
* wrap zip object with list
* use ModelTask from raiutils
* remove prints
* flake8
* isort
* use FORECASTING from raiutils.models
* isort
* fix sktime issue
* get past Python errors
* fix predictions array shape
* fix UI-Python mismatch with renamed time and time series ID column names (now called "features")
* lintfix
* undo changes that aren't needed any longer
* remove AML notebook
* update notebook by removing unnecessary content
* notebook tests, basic setup
* ids for tests
* fix notebook based on latest changes in the repo
* add test for adding a what if case
* flake8 and cleanup
* isort
* lintfix
* fix test
* move notebook
* fix nb path
* fix kernelspec
* add pmdarima dependency
* skip forecasting notebook on py 3.6
* remove commented out line
* skip explanation
* add reason kw arg name
* lintfix
* fix case for vision, text
* fix syntax error
* fix python version comparison since it doesn't work with <=
---------
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
Co-authored-by: Gaurav Gupta <47334368+gaugup@users.noreply.github.com>
Co-authored-by: Gaurav Gupta <gaugup@microsoft.com>
Co-authored-by: John Wang <t-wangjohn@microsoft.com>
2023-08-23 06:46:08 +03:00
|
|
|
"responsibleaidashboard-multiclass-dnn-model-debugging",
|
|
|
|
"responsibleaidashboard-orange-juice-forecasting"
|
2023-08-17 20:13:35 +03:00
|
|
|
];
|
|
|
|
const visionFileNames = [
|
2023-07-17 17:52:05 +03:00
|
|
|
"responsibleaidashboard-fridge-image-classification-model-debugging",
|
2023-07-14 23:10:36 +03:00
|
|
|
"responsibleaidashboard-fridge-multilabel-image-classification-model-debugging",
|
2023-07-13 22:38:49 +03:00
|
|
|
"responsibleaidashboard-fridge-object-detection-model-debugging"
|
2022-01-07 23:24:24 +03:00
|
|
|
];
|
2023-08-23 22:53:13 +03:00
|
|
|
const textFileNames = [
|
2023-08-25 18:38:18 +03:00
|
|
|
"responsibleaidashboard-DBPedia-text-classification-model-debugging",
|
2023-08-30 00:12:16 +03:00
|
|
|
"responsibleaidashboard-blbooksgenre-binary-text-classification-model-debugging",
|
|
|
|
"responsibleaidashboard-covid-event-multilabel-text-classification-model-debugging"
|
2023-08-23 22:53:13 +03:00
|
|
|
];
|
|
|
|
const ignoredFiles = [
|
2023-08-28 17:35:41 +03:00
|
|
|
"responsibleaidashboard-question-answering-model-debugging"
|
2023-08-23 22:53:13 +03:00
|
|
|
];
|
|
|
|
const fileNames = tabularFileNames
|
|
|
|
.concat(visionFileNames)
|
|
|
|
.concat(textFileNames);
|
2022-09-20 08:36:03 +03:00
|
|
|
const notebookHostReg = /^ResponsibleAI started at (http:\/\/localhost:\d+)$/m;
|
|
|
|
const serveHostReg = /Web Development Server is listening at\s+(.*)$/m;
|
2021-10-30 10:37:32 +03:00
|
|
|
const timeout = 3600;
|
|
|
|
|
2023-08-17 20:13:35 +03:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param {string} notebook
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
function getDirForNotebook(notebook) {
|
|
|
|
if (notebook.endsWith(".py")) {
|
|
|
|
notebook = notebook.replace(".py", "");
|
|
|
|
}
|
|
|
|
if (visionFileNames.includes(notebook)) {
|
|
|
|
return visionDir;
|
2023-08-23 22:53:13 +03:00
|
|
|
} else if (textFileNames.includes(notebook)) {
|
|
|
|
return textDir;
|
2023-08-17 20:13:35 +03:00
|
|
|
} else if (tabularFileNames.includes(notebook)) {
|
|
|
|
return tabularDir;
|
|
|
|
} else {
|
|
|
|
throw new Error(`Notebook ${notebook} not found.`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-23 22:53:13 +03:00
|
|
|
function getFilesFromNotebookDirs() {
|
|
|
|
return fs
|
|
|
|
.readdirSync(tabularDir)
|
|
|
|
.concat(fs.readdirSync(visionDir))
|
|
|
|
.concat(fs.readdirSync(textDir));
|
|
|
|
}
|
|
|
|
|
2022-09-20 08:36:03 +03:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param {string} host
|
|
|
|
* @returns {Promise<string>}
|
|
|
|
*/
|
|
|
|
async function serve(host) {
|
|
|
|
console.log(`Running nx serve`);
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
throw new Error(`serve timeout.`);
|
|
|
|
}, timeout * 1000);
|
|
|
|
const nbProcess = spawn("node", [nxPath, "serve", "widget"], {
|
|
|
|
cwd: path.join(__dirname, ".."),
|
|
|
|
env: {
|
|
|
|
NX_based_url: host
|
|
|
|
}
|
|
|
|
});
|
|
|
|
nbProcess.on("exit", () => {
|
|
|
|
throw new Error(`Failed to run serve`);
|
|
|
|
});
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
let stdout = "";
|
|
|
|
const handleOutput = (data) => {
|
|
|
|
const message = data.toString();
|
|
|
|
stdout += message;
|
|
|
|
console.log(message);
|
|
|
|
if (serveHostReg.test(stdout)) {
|
|
|
|
clearTimeout(timer);
|
|
|
|
resolve(serveHostReg.exec(stdout)[1]);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
nbProcess.stdout.on("data", handleOutput);
|
|
|
|
nbProcess.stderr.on("data", handleOutput);
|
|
|
|
nbProcess.stdout.on("error", (error) => {
|
|
|
|
throw error;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-10-30 10:37:32 +03:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param {string} name
|
|
|
|
* @returns {Promise<string>}
|
|
|
|
*/
|
|
|
|
async function runNotebook(name) {
|
|
|
|
console.log(`Running ${name}`);
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
throw new Error(`${name} timeout.`);
|
|
|
|
}, timeout * 1000);
|
2023-08-17 20:13:35 +03:00
|
|
|
const dir = getDirForNotebook(name);
|
|
|
|
const nbProcess = spawn("python", ["-i", path.join(dir, name)]);
|
2021-10-30 10:37:32 +03:00
|
|
|
nbProcess.on("exit", () => {
|
|
|
|
throw new Error(`Failed to run notebook ${name}`);
|
|
|
|
});
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
let stdout = "";
|
|
|
|
const handleOutput = (data) => {
|
|
|
|
const message = data.toString();
|
|
|
|
stdout += message;
|
|
|
|
console.log(message);
|
2022-09-20 08:36:03 +03:00
|
|
|
if (notebookHostReg.test(stdout)) {
|
2021-10-30 10:37:32 +03:00
|
|
|
clearTimeout(timer);
|
2022-09-20 08:36:03 +03:00
|
|
|
resolve(notebookHostReg.exec(stdout)[1]);
|
2021-10-30 10:37:32 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
nbProcess.stdout.on("data", handleOutput);
|
|
|
|
nbProcess.stderr.on("data", handleOutput);
|
|
|
|
nbProcess.stdout.on("error", (error) => {
|
|
|
|
throw error;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-06-24 07:51:07 +03:00
|
|
|
function addFlightsInFile(path, flights) {
|
|
|
|
if (!fs.existsSync(path)) {
|
|
|
|
throw new Error(`${path} does not exist.`);
|
|
|
|
}
|
|
|
|
let content = fs.readFileSync(path, { encoding: "utf-8" });
|
|
|
|
let startIndex = 0;
|
|
|
|
const dashboardConstructorCall = "ResponsibleAIDashboard(";
|
|
|
|
while (startIndex < content.length) {
|
|
|
|
startIndex = content.indexOf(dashboardConstructorCall, startIndex);
|
|
|
|
if (startIndex === -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
let dashboardArgsIndex = startIndex + dashboardConstructorCall.length;
|
|
|
|
let parenthesesBalance = 1;
|
|
|
|
while (parenthesesBalance > 0) {
|
|
|
|
if (content.at(dashboardArgsIndex) === "(") {
|
|
|
|
parenthesesBalance += 1;
|
|
|
|
} else if (content.at(dashboardArgsIndex) === ")") {
|
|
|
|
parenthesesBalance -= 1;
|
|
|
|
}
|
|
|
|
dashboardArgsIndex += 1;
|
|
|
|
}
|
|
|
|
content =
|
|
|
|
content.slice(0, dashboardArgsIndex - 1) +
|
|
|
|
`, feature_flights="${flights.split(",").join("&")}")` +
|
|
|
|
content.slice(dashboardArgsIndex);
|
|
|
|
startIndex = dashboardArgsIndex + 1;
|
|
|
|
}
|
|
|
|
console.log(`writing notebook with flights to ${path}`);
|
|
|
|
fs.writeFileSync(path, content, { encoding: "utf-8" });
|
|
|
|
}
|
|
|
|
|
2022-02-10 02:00:48 +03:00
|
|
|
function checkIfAllNotebooksHaveTests() {
|
2022-06-16 04:04:15 +03:00
|
|
|
console.log(`Checking if all notebooks under ${baseDir} have tests`);
|
2023-08-23 22:53:13 +03:00
|
|
|
const files = getFilesFromNotebookDirs()
|
2022-02-10 02:00:48 +03:00
|
|
|
.filter((f) => f.startsWith(filePrefix) && f.endsWith(".ipynb"))
|
2023-08-23 22:53:13 +03:00
|
|
|
.map((f) => f.replace(".ipynb", ""))
|
|
|
|
.filter((f) => !ignoredFiles.includes(f));
|
2022-02-10 02:00:48 +03:00
|
|
|
const allNotebooksHaveTests = _.isEqual(_.sortBy(files), _.sortBy(fileNames));
|
|
|
|
if (!allNotebooksHaveTests) {
|
|
|
|
throw new Error(
|
2022-06-16 04:04:15 +03:00
|
|
|
`Some of the notebooks don't have tests. If a new notebook is added, Please add tests.`
|
2022-02-10 02:00:48 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
console.log(`All notebooks have tests.`);
|
|
|
|
}
|
|
|
|
|
2022-06-24 07:51:07 +03:00
|
|
|
function convertNotebooks(notebook, flights) {
|
2022-06-16 04:04:15 +03:00
|
|
|
console.log("Converting notebooks");
|
2022-01-07 23:24:24 +03:00
|
|
|
for (var fileName of fileNames) {
|
2022-06-16 04:04:15 +03:00
|
|
|
if (notebook && fileName !== notebook) {
|
|
|
|
console.log(`Skipping ${fileName}. Looking for ${notebook} only.`);
|
|
|
|
continue;
|
|
|
|
}
|
2023-08-17 20:13:35 +03:00
|
|
|
const dir = getDirForNotebook(fileName);
|
2022-06-24 07:51:07 +03:00
|
|
|
if (flights) {
|
|
|
|
// flights were passed (not just -f without flights arg)
|
|
|
|
console.log(
|
|
|
|
`Converting notebook ${fileName} with flights ${flights.toString()}\r\n`
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// no flights were passed
|
|
|
|
console.log(`Converting notebook ${fileName} with no flights.\r\n`);
|
|
|
|
}
|
2022-01-07 23:24:24 +03:00
|
|
|
const { status, stderr } = spawnSync(
|
|
|
|
"jupyter",
|
2023-08-17 20:13:35 +03:00
|
|
|
["nbconvert", path.join(dir, `${fileName}.ipynb`), "--to", "script"],
|
2022-01-07 23:24:24 +03:00
|
|
|
{
|
|
|
|
stdio: "inherit"
|
|
|
|
}
|
|
|
|
);
|
|
|
|
if (status) {
|
|
|
|
throw new Error(`Failed to convert notebook:\r\n\r\n${stderr}`);
|
2021-10-30 10:37:32 +03:00
|
|
|
}
|
2022-06-24 07:51:07 +03:00
|
|
|
if (flights) {
|
2023-08-17 20:13:35 +03:00
|
|
|
addFlightsInFile(path.join(dir, `${fileName}.py`), flights);
|
2022-06-24 07:51:07 +03:00
|
|
|
}
|
2022-01-07 23:24:24 +03:00
|
|
|
console.log(`Converted notebook ${fileName}\r\n`);
|
2021-10-30 10:37:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @typedef {Object} Host
|
|
|
|
* @property {string} file
|
|
|
|
* @property {string} host
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* @returns {Host[]}
|
|
|
|
*/
|
2022-09-20 08:36:03 +03:00
|
|
|
async function runNotebooks(selectedNotebook, host) {
|
2023-08-23 22:53:13 +03:00
|
|
|
let files = getFilesFromNotebookDirs().filter(
|
|
|
|
(f) => f.startsWith(filePrefix) && f.endsWith(".py")
|
|
|
|
);
|
2022-06-16 04:04:15 +03:00
|
|
|
console.log("Available notebooks:");
|
|
|
|
files.forEach((file) => {
|
|
|
|
console.log(` ${file}`);
|
|
|
|
});
|
|
|
|
if (selectedNotebook) {
|
|
|
|
const nbFileName = `${selectedNotebook}.py`;
|
2022-09-23 00:38:57 +03:00
|
|
|
if (host) {
|
|
|
|
files = [nbFileName];
|
|
|
|
} else {
|
|
|
|
console.log(`Should only run ${nbFileName}`);
|
|
|
|
files = files.filter((f) => f === nbFileName);
|
|
|
|
if (files.length === 0) {
|
|
|
|
console.log(`Could not find any matching notebook for ${nbFileName}.`);
|
|
|
|
exit(1);
|
|
|
|
}
|
2022-06-16 04:04:15 +03:00
|
|
|
}
|
|
|
|
}
|
2021-10-30 10:37:32 +03:00
|
|
|
const hosts = [];
|
|
|
|
for (const f of files) {
|
2022-09-20 08:36:03 +03:00
|
|
|
host = host || (await runNotebook(f));
|
2022-06-16 04:04:15 +03:00
|
|
|
hosts.push({ file: f, host: host });
|
|
|
|
console.log(`file: ${f}, host: ${host}`);
|
2021-10-30 10:37:32 +03:00
|
|
|
}
|
|
|
|
return hosts;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param {Host[]} hosts
|
|
|
|
*/
|
|
|
|
function writeCypressSettings(hosts) {
|
|
|
|
fs.writeFileSync(
|
|
|
|
path.join(__dirname, "../apps/widget-e2e/cypress.env.json"),
|
|
|
|
JSON.stringify({
|
|
|
|
hosts
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-09-20 08:36:03 +03:00
|
|
|
function e2e(watch, selectedNotebook, flights, host) {
|
2022-07-22 20:39:37 +03:00
|
|
|
console.log(`Running e2e for notebook ${selectedNotebook}`);
|
2022-06-16 04:04:15 +03:00
|
|
|
let notebookArgs = [];
|
|
|
|
if (selectedNotebook) {
|
|
|
|
// remove prefix "responsibleaidashboard"
|
|
|
|
// remove dashes and make camel case
|
|
|
|
let notebookKey = selectedNotebook.substring(
|
|
|
|
"responsibleaidashboard".length,
|
|
|
|
selectedNotebook.length
|
|
|
|
);
|
|
|
|
notebookKey = notebookKey
|
|
|
|
.split("-")
|
|
|
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
|
|
.join("");
|
2022-06-24 07:51:07 +03:00
|
|
|
if (flights) {
|
|
|
|
// append flights (if any) with first letter capitalized
|
|
|
|
notebookKey =
|
|
|
|
notebookKey +
|
|
|
|
flights
|
|
|
|
.split(",")
|
|
|
|
.map((flight) => flight.charAt(0).toUpperCase() + flight.slice(1))
|
|
|
|
.join("");
|
|
|
|
}
|
2022-06-16 04:04:15 +03:00
|
|
|
console.log(
|
|
|
|
`Determined notebook key ${notebookKey} for notebook ${selectedNotebook}.`
|
|
|
|
);
|
|
|
|
notebookArgs = ["--spec", `**/responsibleaitoolbox${notebookKey}/**`];
|
|
|
|
}
|
2021-10-30 10:37:32 +03:00
|
|
|
const { status, stderr } = spawnSync(
|
|
|
|
"node",
|
2022-06-16 04:04:15 +03:00
|
|
|
[
|
|
|
|
nxPath,
|
|
|
|
"e2e",
|
|
|
|
"widget-e2e",
|
|
|
|
...notebookArgs,
|
|
|
|
watch ? "--watch" : undefined
|
|
|
|
],
|
2021-10-30 10:37:32 +03:00
|
|
|
{
|
|
|
|
stdio: "inherit",
|
|
|
|
cwd: path.join(__dirname, "..")
|
|
|
|
}
|
|
|
|
);
|
|
|
|
if (status) {
|
|
|
|
throw new Error(`Failed to run e2e:\r\n\r\n${stderr}`);
|
|
|
|
}
|
2022-06-16 04:04:15 +03:00
|
|
|
console.log("e2e finished\r\n");
|
2021-10-30 10:37:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
async function main() {
|
|
|
|
commander
|
|
|
|
.option("-w, --watch", "Watch mode")
|
2022-06-16 04:04:15 +03:00
|
|
|
.option("--skipgen", "Skip notebook generation")
|
2022-09-20 08:36:03 +03:00
|
|
|
.option(
|
|
|
|
"--host [host]",
|
2023-02-12 08:14:10 +03:00
|
|
|
"Skip notebook running and use host provided to run e2e: use full url 'http://localhost:5000' or port number"
|
2022-09-20 08:36:03 +03:00
|
|
|
)
|
2022-06-16 04:04:15 +03:00
|
|
|
.option("-n, --notebook [notebook]", "Run specific notebook")
|
2022-06-24 07:51:07 +03:00
|
|
|
.option(
|
|
|
|
"-f, --flights [flights]",
|
|
|
|
"Use flights separated by comma (no whitespace). Not specifying flights means that no flights are used."
|
|
|
|
)
|
2021-10-30 10:37:32 +03:00
|
|
|
.parse(process.argv)
|
|
|
|
.outputHelp();
|
2022-06-24 07:51:07 +03:00
|
|
|
const skipgen = commander.opts().skipgen;
|
2022-09-20 08:36:03 +03:00
|
|
|
const watch = commander.opts().watch;
|
|
|
|
let host = commander.opts().host;
|
|
|
|
if (host && !isNaN(parseInt(host))) {
|
|
|
|
host = `http://localhost:${host}`;
|
|
|
|
}
|
2022-06-24 07:51:07 +03:00
|
|
|
const notebook = commander.opts().notebook;
|
2022-09-20 08:36:03 +03:00
|
|
|
if (host && !notebook) {
|
|
|
|
throw new Error("Notebook is required when host is specified.");
|
|
|
|
}
|
2022-06-24 07:51:07 +03:00
|
|
|
let flights = commander.opts().flights;
|
2022-07-22 20:39:37 +03:00
|
|
|
console.log("Checking flights: " + flights);
|
2022-07-01 09:00:16 +03:00
|
|
|
if (flights?.toString() === "true") {
|
2022-06-24 07:51:07 +03:00
|
|
|
// -f passed without arguments
|
|
|
|
flights = undefined;
|
2022-07-22 20:39:37 +03:00
|
|
|
console.log("setting flights to undefined!!!");
|
2022-06-24 07:51:07 +03:00
|
|
|
}
|
2022-02-10 02:00:48 +03:00
|
|
|
checkIfAllNotebooksHaveTests();
|
2022-09-20 08:36:03 +03:00
|
|
|
if (!skipgen && !host) {
|
2022-06-24 07:51:07 +03:00
|
|
|
convertNotebooks(notebook, flights);
|
2022-09-20 08:36:03 +03:00
|
|
|
} else {
|
|
|
|
console.log("Skipping converting notebooks", skipgen, host);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (host && watch) {
|
|
|
|
host = await serve(host);
|
2022-06-16 04:04:15 +03:00
|
|
|
}
|
2022-09-20 08:36:03 +03:00
|
|
|
console.log(`Running watch mode on ${host}`);
|
|
|
|
|
|
|
|
const hosts = await runNotebooks(notebook, host);
|
2021-10-30 10:37:32 +03:00
|
|
|
writeCypressSettings(hosts);
|
2022-07-22 20:39:37 +03:00
|
|
|
for (var fileName of fileNames) {
|
|
|
|
if (notebook && fileName !== notebook) {
|
|
|
|
console.log(
|
|
|
|
`Skipping e2e for ${fileName}. Looking for ${notebook} only.`
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
2022-09-20 08:36:03 +03:00
|
|
|
e2e(watch, fileName, flights, host);
|
2022-07-22 20:39:37 +03:00
|
|
|
}
|
2021-10-30 10:37:32 +03:00
|
|
|
process.exit(0);
|
|
|
|
}
|
2022-01-07 23:24:24 +03:00
|
|
|
|
2021-10-30 10:37:32 +03:00
|
|
|
function onExit() {
|
|
|
|
console.log("Existing e2e");
|
|
|
|
}
|
|
|
|
async function onExitRequested() {
|
|
|
|
process.exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
main();
|
|
|
|
|
|
|
|
process.stdin.resume();
|
|
|
|
process.on("SIGINT", onExitRequested);
|
|
|
|
|
|
|
|
// catches "kill pid" (for example: nodemon restart)
|
|
|
|
process.on("SIGUSR1", onExitRequested);
|
|
|
|
process.on("SIGUSR2", onExitRequested);
|
|
|
|
|
|
|
|
process.on("exit", onExit);
|