Upgrades people!
This commit is contained in:
parent
f84234150f
commit
8ad56e8d38
40 changed files with 483 additions and 379 deletions
BIN
bin/executor
BIN
bin/executor
Binary file not shown.
2
dist/bundles/qualiteer-executor.mjs
vendored
2
dist/bundles/qualiteer-executor.mjs
vendored
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
||||||
export default function executorConfig(args){
|
export default function executorConfig(payload){
|
||||||
return {
|
return {
|
||||||
command: ()=> args.slice(2),
|
command: ({command})=> command,
|
||||||
url: process.env.QUALITEER_URL,
|
url: ({url})=>url ,
|
||||||
jobId: () => args[1],
|
jobId: ({jobId}) => jobId,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,13 +10,15 @@ const table = "silenced_tests";
|
||||||
const PG_DISABLED = process.env.POSTGRES_DISABLED;
|
const PG_DISABLED = process.env.POSTGRES_DISABLED;
|
||||||
|
|
||||||
const silencedMock = () => {
|
const silencedMock = () => {
|
||||||
return [{
|
return [
|
||||||
name: `failing`,
|
{
|
||||||
class: `failing.js`,
|
name: `failing`,
|
||||||
method: "FAKEMETHOD",
|
class: `failing.js`,
|
||||||
id: 0,
|
method: "FAKEMETHOD",
|
||||||
silencedUntil: new Date().toJSON(),
|
id: 0,
|
||||||
}]
|
silencedUntil: new Date().toJSON(),
|
||||||
|
},
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Queries
|
// Queries
|
||||||
|
|
|
@ -20,12 +20,12 @@ const testsMock = () => {
|
||||||
isCompound: false,
|
isCompound: false,
|
||||||
type: "api",
|
type: "api",
|
||||||
description: "This is a single test",
|
description: "This is a single test",
|
||||||
tags: ["cron_1hour","reg_us", "env_ci", "proj_core", "skip_alt"],
|
tags: ["cron_1hour", "reg_us", "env_ci", "proj_core", "skip_alt"],
|
||||||
path: "tests/assets/suite/single.js",
|
path: "tests/assets/suite/single.js",
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
mergeRequest: "https://example.com"
|
mergeRequest: "https://example.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "failing",
|
name: "failing",
|
||||||
class: "failing.js",
|
class: "failing.js",
|
||||||
|
@ -33,12 +33,12 @@ const testsMock = () => {
|
||||||
isCompound: false,
|
isCompound: false,
|
||||||
type: "ui",
|
type: "ui",
|
||||||
description: "This is a failing test",
|
description: "This is a failing test",
|
||||||
tags: ["cron_1hour","reg_us", "env_ci", "proj_core"],
|
tags: ["cron_1hour", "reg_us", "env_ci", "proj_core"],
|
||||||
path: "tests/assets/suite/failing.js",
|
path: "tests/assets/suite/failing.js",
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
mergeRequest: "https://example.com"
|
mergeRequest: "https://example.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: "primary",
|
name: "primary",
|
||||||
class: "primary.js",
|
class: "primary.js",
|
||||||
|
@ -46,11 +46,18 @@ const testsMock = () => {
|
||||||
isCompound: true,
|
isCompound: true,
|
||||||
type: "api",
|
type: "api",
|
||||||
description: "This is a primary test",
|
description: "This is a primary test",
|
||||||
tags: ["cron_1hour","reg_us", "proj_core", "skip_alt", "compound_secondary"],
|
tags: [
|
||||||
|
"cron_1hour",
|
||||||
|
"reg_us",
|
||||||
|
"proj_core",
|
||||||
|
"skip_alt",
|
||||||
|
"compound_secondary",
|
||||||
|
],
|
||||||
path: "tests/assets/suite/primary.js",
|
path: "tests/assets/suite/primary.js",
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
mergeRequest: "https://example.com"
|
mergeRequest: "https://example.com",
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: "secondary",
|
name: "secondary",
|
||||||
class: "secondary.js",
|
class: "secondary.js",
|
||||||
|
@ -58,12 +65,12 @@ const testsMock = () => {
|
||||||
isCompound: true,
|
isCompound: true,
|
||||||
type: "api",
|
type: "api",
|
||||||
description: "This is a secondary test",
|
description: "This is a secondary test",
|
||||||
tags: ["cron_1hour","reg_us", "proj_core", "compound_tertiary"],
|
tags: ["cron_1hour", "reg_us", "proj_core", "compound_tertiary"],
|
||||||
path: "tests/assets/suite/secondary.js",
|
path: "tests/assets/suite/secondary.js",
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
mergeRequest: "https://example.com"
|
mergeRequest: "https://example.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
name: "tertiary",
|
name: "tertiary",
|
||||||
class: "tertiary.js",
|
class: "tertiary.js",
|
||||||
|
@ -71,21 +78,21 @@ const testsMock = () => {
|
||||||
isCompound: true,
|
isCompound: true,
|
||||||
type: "api",
|
type: "api",
|
||||||
description: "This is a single test",
|
description: "This is a single test",
|
||||||
tags: ["cron_1hour","reg_us", "proj_core"],
|
tags: ["cron_1hour", "reg_us", "proj_core"],
|
||||||
path: "tests/assets/suite/tertiary.js",
|
path: "tests/assets/suite/tertiary.js",
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
mergeRequest: "https://example.com"
|
mergeRequest: "https://example.com",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const mappingsMock = () => {
|
const mappingsMock = () => {
|
||||||
return [
|
return [
|
||||||
["primary", "secondary1", "tertiary1"],
|
["primary", "secondary1", "tertiary1"],
|
||||||
["primary", "secondary1", "tertiary2"],
|
["primary", "secondary1", "tertiary2"],
|
||||||
["primary", "secondary2", "tertiary3"],
|
["primary", "secondary2", "tertiary3"],
|
||||||
];
|
];
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getTests = async () => {
|
export const getTests = async () => {
|
||||||
if (PG_DISABLED) return testsMock();
|
if (PG_DISABLED) return testsMock();
|
||||||
|
|
|
@ -10,33 +10,35 @@ import {
|
||||||
const table = "test_results";
|
const table = "test_results";
|
||||||
const PG_DISABLED = process.env.POSTGRES_DISABLED;
|
const PG_DISABLED = process.env.POSTGRES_DISABLED;
|
||||||
|
|
||||||
|
|
||||||
const failingMock = () => {
|
const failingMock = () => {
|
||||||
return [{
|
return [
|
||||||
name: "failing",
|
{
|
||||||
class: "failing.js",
|
name: "failing",
|
||||||
timestamp: new Date().toJSON(),
|
class: "failing.js",
|
||||||
method: "FAKEMETHOD",
|
timestamp: new Date().toJSON(),
|
||||||
cron: "1hour",
|
method: "FAKEMETHOD",
|
||||||
type: "api",
|
cron: "1hour",
|
||||||
dailyFails: 12,
|
type: "api",
|
||||||
screenshot: "https://picsum.photos/1920/1080",
|
dailyFails: 12,
|
||||||
recentResults: [1, 0, 0, 1, 0],
|
screenshot: "https://picsum.photos/1920/1080",
|
||||||
isCompound: false,
|
recentResults: [1, 0, 0, 1, 0],
|
||||||
failedMessage: `Some Test FailureMessage`,
|
isCompound: false,
|
||||||
},{
|
failedMessage: `Some Test FailureMessage`,
|
||||||
name: "secondary",
|
},
|
||||||
class: "secondary.js",
|
{
|
||||||
timestamp: new Date().toJSON(),
|
name: "secondary",
|
||||||
method: "FAKEMETHOD",
|
class: "secondary.js",
|
||||||
cron: "1hour",
|
timestamp: new Date().toJSON(),
|
||||||
type: "api",
|
method: "FAKEMETHOD",
|
||||||
dailyFails: 1,
|
cron: "1hour",
|
||||||
screenshot: "https://picsum.photos/1920/1080",
|
type: "api",
|
||||||
recentResults: [1, 0, 0, 1, 0],
|
dailyFails: 1,
|
||||||
isCompound: true,
|
screenshot: "https://picsum.photos/1920/1080",
|
||||||
failedMessage: `Some Test FailureMessage from Secondary`,
|
recentResults: [1, 0, 0, 1, 0],
|
||||||
}]
|
isCompound: true,
|
||||||
|
failedMessage: `Some Test FailureMessage from Secondary`,
|
||||||
|
},
|
||||||
|
];
|
||||||
};
|
};
|
||||||
// Queries
|
// Queries
|
||||||
export const insertTestResult = (testResult) => {
|
export const insertTestResult = (testResult) => {
|
||||||
|
|
|
@ -7,7 +7,8 @@ const { default: executorConfig } = await import(
|
||||||
|
|
||||||
// Load config and args
|
// Load config and args
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const config = normalize(executorConfig(args));
|
const payload = JSON.parse(Buffer.from(args[0], "base64").toString("utf8"));
|
||||||
|
const config = normalize(executorConfig(payload));
|
||||||
// Start Executor
|
// Start Executor
|
||||||
const exec = new Executor(args, config);
|
const exec = new Executor(config, payload);
|
||||||
exec.runJob();
|
exec.runJob();
|
||||||
|
|
|
@ -34,9 +34,15 @@ const pipelineMaxLife = (testName) => {
|
||||||
|
|
||||||
const buildCompound = (jobReq, socketId) => {
|
const buildCompound = (jobReq, socketId) => {
|
||||||
const { testName, command } = jobReq;
|
const { testName, command } = jobReq;
|
||||||
const pipelineTriggers = jobReq.pipelineTriggers;
|
const { pipeline } = jobReq;
|
||||||
if (pipelineTriggers) command.push(`pipelineTriggers=${pipelineTriggers}`);
|
if (pipeline) {
|
||||||
command.push(`pipelineDashboardSocket=${socketId}`);
|
pipeline.dashboardSocketId = socketId;
|
||||||
|
const pipelineArg = Buffer.from(JSON.stringify(pipeline), "utf8").toString(
|
||||||
|
"base64"
|
||||||
|
);
|
||||||
|
command.push(`pipeline=${pipelineArg}`);
|
||||||
|
}
|
||||||
|
|
||||||
return { ...jobReq, command };
|
return { ...jobReq, command };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ const { command } = job.spec.template.spec.containers[0];
|
||||||
INFO("EXEC", "Internal Executor Starting!");
|
INFO("EXEC", "Internal Executor Starting!");
|
||||||
cp.exec(command, (error, stdout, stderr) => {
|
cp.exec(command, (error, stdout, stderr) => {
|
||||||
if (error) ERR("EXEC", error);
|
if (error) ERR("EXEC", error);
|
||||||
//if(stdout) VERB("EXEC-STDOUT", stdout);
|
//if (stdout) VERB("EXEC-STDOUT", stdout);
|
||||||
//if(stderr) VERB("EXEC-STDERR", stderr);
|
//if (stderr) VERB("EXEC-STDERR", stderr);
|
||||||
OK("EXEC", "Internal Executor Finished!");
|
OK("EXEC", "Internal Executor Finished!");
|
||||||
process.exit(error ? 1 : 0);
|
process.exit(error ? 1 : 0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,9 +22,11 @@ const wrapCommand = (jobId, command) => {
|
||||||
? `node ${executorBin}`
|
? `node ${executorBin}`
|
||||||
: `chmod +x ${executorBin} && ./${executorBin}`;
|
: `chmod +x ${executorBin} && ./${executorBin}`;
|
||||||
const cmd = command.map((arg) => JSON.stringify(arg));
|
const cmd = command.map((arg) => JSON.stringify(arg));
|
||||||
const curlCmd = `curl -o qltr-executor ${executorUrl} && ${bin} ${qualiteerUrl} ${jobId} ${cmd.join(
|
const payload = Buffer.from(
|
||||||
" "
|
JSON.stringify({ jobId, command, url: qualiteerUrl }),
|
||||||
)}`;
|
"utf8"
|
||||||
|
).toString("base64");
|
||||||
|
const curlCmd = `curl -o qltr-executor ${executorUrl} && ${bin} ${payload}`;
|
||||||
return curlCmd;
|
return curlCmd;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,25 +7,25 @@ const nest = (arr) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const asTree = (branches) => {
|
export const asTree = (branches) => {
|
||||||
const nests = branches.map((b)=>nest(b));
|
const nests = branches.map((b) => nest(b));
|
||||||
return _.merge(...nests);
|
return _.merge(...nests);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const asBranches = (array) => {
|
export const asBranches = (array) => {
|
||||||
const merged = [];
|
const merged = [];
|
||||||
array.forEach((p, i) => {
|
array.forEach((p, i) => {
|
||||||
p.forEach((v, i) => {
|
p.forEach((v, i) => {
|
||||||
if (!merged[i]) merged[i] = [];
|
if (!merged[i]) merged[i] = [];
|
||||||
if (!merged[i].includes(v)) merged[i].push(v);
|
if (!merged[i].includes(v)) merged[i].push(v);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return merged;
|
});
|
||||||
}
|
return merged;
|
||||||
|
};
|
||||||
|
|
||||||
export const as1d = (a) => [].concat.apply([], a);
|
export const as1d = (a) => [].concat.apply([], a);
|
||||||
|
|
||||||
export const selectBranch = (map,test) => {
|
export const selectBranch = (map, test) => {
|
||||||
const pipeline = map.find((pm)=>pm.includes(test));
|
const pipeline = map.find((pm) => pm.includes(test));
|
||||||
const testIndex = pipeline.findIndex((t) => t === test);
|
const testIndex = pipeline.findIndex((t) => t === test);
|
||||||
return pipeline.slice(0, testIndex + 1);
|
return pipeline.slice(0, testIndex + 1);
|
||||||
}
|
};
|
||||||
|
|
|
@ -24,21 +24,18 @@ export default class TestResultsWorker extends Worker {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
onMessage(testResult) {
|
onMessage(testResult) {
|
||||||
const { pipelineData, pipelineTriggers, pipelineDelay } = testResult;
|
const { pipeline } = testResult;
|
||||||
const pipelineTrigger = { pipelineData, pipelineTriggers, pipelineDelay };
|
|
||||||
// Alter to start next test
|
// Alter to start next test
|
||||||
// TODO the delay should be autopopulated either by the suite, or filled in by the server
|
// TODO the delay should be autopopulated either by the suite, or filled in by the server
|
||||||
if (pipelineTriggers)
|
if (pipeline) return this.pipelineTrigger(pipeline);
|
||||||
return this.pipelineTrigger(
|
const { dashboardSocketId: dsi } = pipeline;
|
||||||
pipelineTrigger,
|
this.pipelineClose(dsi);
|
||||||
testResult.pipelineDashboardSocket
|
|
||||||
);
|
|
||||||
this.pipelineClose(testResult.pipelineDashboardSocket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pipelineTrigger(pipelineTrigger, socketId) {
|
pipelineTrigger(pipeline) {
|
||||||
pipelineTrigger.pipelineDelay = 1000 * 5;
|
const { dashboardSocketId: dsi } = pipeline;
|
||||||
this.skio.to(socketId).emit(evt.PPL_TRG, pipelineTrigger);
|
this.skio.to(dsi).emit(evt.PPL_TRG, pipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
pipelineClose(socketId) {
|
pipelineClose(socketId) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ router.use(jsonMiddleware());
|
||||||
|
|
||||||
// Get Routes
|
// Get Routes
|
||||||
router.get("/silenced", (req, res) => {
|
router.get("/silenced", (req, res) => {
|
||||||
getSilencedTests().then((t)=>res.send(t));
|
getSilencedTests().then((t) => res.send(t));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Post Routes
|
// Post Routes
|
||||||
|
|
|
@ -9,12 +9,12 @@ router.use(jsonMiddleware({ limit: maxSize }));
|
||||||
|
|
||||||
// Get Routes
|
// Get Routes
|
||||||
router.get("/tests", (req, res) => {
|
router.get("/tests", (req, res) => {
|
||||||
getTests().then((t)=>res.json(t));
|
getTests().then((t) => res.json(t));
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/pipeline-mappings", (req,res)=>{
|
router.get("/pipeline-mappings", (req, res) => {
|
||||||
getPipelineMappings().then((m)=>res.json(m));
|
getPipelineMappings().then((m) => res.json(m));
|
||||||
})
|
});
|
||||||
|
|
||||||
// Post Routes
|
// Post Routes
|
||||||
router.post("/update", (req, res) => {
|
router.post("/update", (req, res) => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ router.use(jsonMiddleware());
|
||||||
|
|
||||||
// Get Routes
|
// Get Routes
|
||||||
router.get("/failing", (req, res) => {
|
router.get("/failing", (req, res) => {
|
||||||
getCurrentlyFailing().then((f)=>res.json(f));
|
getCurrentlyFailing().then((f) => res.json(f));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Post Routes
|
// Post Routes
|
||||||
|
|
|
@ -12,10 +12,10 @@ const ERR = "e";
|
||||||
const OUT = "o";
|
const OUT = "o";
|
||||||
|
|
||||||
export default class Executor {
|
export default class Executor {
|
||||||
constructor(args, config, options = {}) {
|
constructor(config, payload) {
|
||||||
this.url = config.url(args) ?? process.env.QUALITEER_URL;
|
this.url = config.url(payload) ?? process.env.QUALITEER_URL;
|
||||||
this.jobId = config.jobId(args) ?? process.env.QUALITEER_JOB_ID;
|
this.jobId = config.jobId(payload) ?? process.env.QUALITEER_JOB_ID;
|
||||||
this.command = config.command(args) ?? process.env.QUALITEER_COMMAND;
|
this.command = config.command(payload) ?? process.env.QUALITEER_COMMAND;
|
||||||
this.mode = modes.EXEC;
|
this.mode = modes.EXEC;
|
||||||
|
|
||||||
// Internal Buffer
|
// Internal Buffer
|
||||||
|
@ -24,10 +24,10 @@ export default class Executor {
|
||||||
this.buf[OUT] = "";
|
this.buf[OUT] = "";
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
this.spawn = options.spawn ?? this.spawn.bind(this);
|
this.spawn = this.spawn.bind(this);
|
||||||
this.report = options.report ?? this.report.bind(this);
|
this.report = this.report.bind(this);
|
||||||
this.onProcClose = options.onProcClose ?? this.onProcClose.bind(this);
|
this.onProcClose = this.onProcClose.bind(this);
|
||||||
this.onClose = options.onClose ?? this.onClose.bind(this);
|
this.onClose = this.onClose.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
spawn() {
|
spawn() {
|
||||||
|
|
|
@ -54,29 +54,32 @@ export default class Initiator {
|
||||||
onCreate = onCreate ?? this.onCreate.bind(this);
|
onCreate = onCreate ?? this.onCreate.bind(this);
|
||||||
onPipelineTrigger =
|
onPipelineTrigger =
|
||||||
onPipelineTrigger ??
|
onPipelineTrigger ??
|
||||||
((trigger) => {
|
((pipeline) => {
|
||||||
console.log("job trg:", trigger);
|
console.log("job trg:", pipeline);
|
||||||
const testName = trigger.pipelineTriggers;
|
const { triggers } = pipeline;
|
||||||
const pipelineData = trigger.pipelineData;
|
if (!Object.keys(triggers).length) onPipelineClose();
|
||||||
const pipelineTriggers = trigger.newPipelineTriggers;
|
// For each trigger
|
||||||
const jobReq = {
|
for (var testName in triggers) {
|
||||||
...jobRequest,
|
const delay = triggers[testName].__testDelay ?? 0;
|
||||||
testName,
|
delete triggers[testName].__testDelay;
|
||||||
pipelineData,
|
const jobReq = {
|
||||||
pipelineTriggers,
|
...jobRequest,
|
||||||
};
|
pipeline: { ...pipeline, triggers: triggers[testName] },
|
||||||
setTimeout(
|
testName,
|
||||||
() =>
|
};
|
||||||
this.newPipelineJob(
|
setTimeout(
|
||||||
jobReq,
|
() =>
|
||||||
onLog,
|
this.newPipelineJob(
|
||||||
onClose,
|
jobReq,
|
||||||
onCreate,
|
onLog,
|
||||||
onPipelineTrigger,
|
onClose,
|
||||||
onPipelineClose
|
onCreate,
|
||||||
),
|
onPipelineTrigger,
|
||||||
trigger.pipelineDelay
|
onPipelineClose
|
||||||
);
|
),
|
||||||
|
delay
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
onPipelineClose = onPipelineClose ?? this.onPipelineClose.bind(this);
|
onPipelineClose = onPipelineClose ?? this.onPipelineClose.bind(this);
|
||||||
this.sk = mgr.socket("/");
|
this.sk = mgr.socket("/");
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
// Import Contexts
|
// Import Contexts
|
||||||
import { QueryClient, QueryClientProvider} from '@tanstack/react-query'
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { JobProvider } from "./ctx/JobContext.jsx";
|
import { JobProvider } from "./ctx/JobContext.jsx";
|
||||||
import { StoreProvider } from "./ctx/StoreContext.jsx";
|
import { StoreProvider } from "./ctx/StoreContext.jsx";
|
||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
// Import Views
|
// Import Views
|
||||||
import Views from "./views/Views.jsx";
|
import Views from "./views/Views.jsx";
|
||||||
|
|
||||||
const queryClient = new QueryClient()
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
return (
|
return (
|
||||||
<div className="qualiteer">
|
<div className="qualiteer">
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<StoreProvider>
|
<StoreProvider>
|
||||||
<JobProvider>
|
<JobProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Views />
|
<Views />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</JobProvider>
|
</JobProvider>
|
||||||
</StoreProvider>
|
</StoreProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
const QUALITEER_URL = "https://qualiteer.elijahparker3.repl.co/api";
|
const QUALITEER_URL = "https://qualiteer.elijahparker3.repl.co/api";
|
||||||
|
|
||||||
const useMock = false;
|
const useMock = true;
|
||||||
|
|
||||||
const asMock = (data) => ({ data });
|
const asMock = (data) => ({ data });
|
||||||
|
|
||||||
const fetchApi = (subPath)=> async ()=> fetch(`${QUALITEER_URL}${subPath}`).then((res)=>res.json());
|
const fetchApi = (subPath) => async () =>
|
||||||
|
fetch(`${QUALITEER_URL}${subPath}`).then((res) => res.json());
|
||||||
|
|
||||||
export const useCatalogTests = () => useMock ? asMock([]): useQuery(['catalogTests'], fetchApi("/catalog/tests")
|
export const useCatalogTests = () =>
|
||||||
)
|
useMock ? asMock([]) : useQuery(["catalogTests"], fetchApi("/catalog/tests"));
|
||||||
|
|
||||||
export const usePipelineMappings = () => useMock ? asMock([
|
export const usePipelineMappings = () =>
|
||||||
["primary", "secondary1", "tertiary1"],
|
useMock
|
||||||
["primary", "secondary1", "tertiary2"],
|
? asMock([
|
||||||
["primary", "secondary2", "tertiary3"],
|
["primary", "secondary1", "tertiary1"],
|
||||||
]) : useQuery(['pipelineMappings'], fetchApi("/catalog/pipeline-mappings")
|
["primary", "secondary1", "tertiary2"],
|
||||||
)
|
["primary", "secondary2", "tertiary3"],
|
||||||
|
])
|
||||||
|
: useQuery(["pipelineMappings"], fetchApi("/catalog/pipeline-mappings"));
|
||||||
|
|
||||||
export const useSilencedAlerts = () => useMock? asMock([]) : useQuery(['silenced'], fetchApi("/alerting/silenced")
|
export const useSilencedAlerts = () =>
|
||||||
)
|
useMock ? asMock([]) : useQuery(["silenced"], fetchApi("/alerting/silenced"));
|
||||||
|
|
||||||
export const useCurrentlyFailing = () => useMock? asMock([]) : useQuery(['failing'], fetchApi("/results/failing")
|
export const useCurrentlyFailing = () =>
|
||||||
)
|
useMock ? asMock([]) : useQuery(["failing"], fetchApi("/results/failing"));
|
||||||
|
|
|
@ -15,7 +15,7 @@ const ACTIONS = {
|
||||||
CREATE: "c",
|
CREATE: "c",
|
||||||
UPDATE: "u",
|
UPDATE: "u",
|
||||||
DELETE: "d",
|
DELETE: "d",
|
||||||
PIPELINE: "p"
|
PIPELINE: "p",
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = "https://qualiteer.elijahparker3.repl.co/";
|
const url = "https://qualiteer.elijahparker3.repl.co/";
|
||||||
|
@ -44,7 +44,9 @@ const reducer = (state, action) => {
|
||||||
return { ...state, jobs };
|
return { ...state, jobs };
|
||||||
|
|
||||||
case ACTIONS.UPDATE:
|
case ACTIONS.UPDATE:
|
||||||
jobIndex = jobs.findIndex((j) => j.jobId === (action.job.jobId ?? action.jobId));
|
jobIndex = jobs.findIndex(
|
||||||
|
(j) => j.jobId === (action.job.jobId ?? action.jobId)
|
||||||
|
);
|
||||||
jobs[jobIndex] = { ...jobs[jobIndex], ...action.job };
|
jobs[jobIndex] = { ...jobs[jobIndex], ...action.job };
|
||||||
return { ...state, jobs };
|
return { ...state, jobs };
|
||||||
|
|
||||||
|
@ -54,7 +56,7 @@ const reducer = (state, action) => {
|
||||||
return { ...state, jobs };
|
return { ...state, jobs };
|
||||||
|
|
||||||
case ACTIONS.PIPELINE:
|
case ACTIONS.PIPELINE:
|
||||||
return {...state, pipelines};
|
return { ...state, pipelines };
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +71,8 @@ export const JobProvider = ({ children }) => {
|
||||||
dispatch({ type: ACTIONS.CREATE, job: { ...job, log: [] } });
|
dispatch({ type: ACTIONS.CREATE, job: { ...job, log: [] } });
|
||||||
const jobDelete = (jobId) => dispatch({ type: ACTIONS.DELETE, jobId });
|
const jobDelete = (jobId) => dispatch({ type: ACTIONS.DELETE, jobId });
|
||||||
|
|
||||||
const updatePipelines = (pipelines) => dispatch({type: ACTIONS.pipeline, pipelines});
|
const updatePipelines = (pipelines) =>
|
||||||
|
dispatch({ type: ACTIONS.pipeline, pipelines });
|
||||||
|
|
||||||
function retryAll(failing) {
|
function retryAll(failing) {
|
||||||
// Query Full Locator
|
// Query Full Locator
|
||||||
|
@ -77,7 +80,7 @@ export const JobProvider = ({ children }) => {
|
||||||
return jobFactory({ testNames: ["single"] });
|
return jobFactory({ testNames: ["single"] });
|
||||||
}
|
}
|
||||||
|
|
||||||
function pipelineComponentJob(jobPipeline, testName){
|
function pipelineComponentJob(jobPipeline, testName) {
|
||||||
const i = new Initiator(url);
|
const i = new Initiator(url);
|
||||||
const jobId = `j${Date.now()}`;
|
const jobId = `j${Date.now()}`;
|
||||||
const job = {
|
const job = {
|
||||||
|
@ -87,7 +90,7 @@ export const JobProvider = ({ children }) => {
|
||||||
isPipeline: true,
|
isPipeline: true,
|
||||||
builderCache: {},
|
builderCache: {},
|
||||||
initiator: i,
|
initiator: i,
|
||||||
}
|
};
|
||||||
const request = {
|
const request = {
|
||||||
testName,
|
testName,
|
||||||
image: "node",
|
image: "node",
|
||||||
|
@ -97,7 +100,7 @@ export const JobProvider = ({ children }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
jobCreate(job);
|
jobCreate(job);
|
||||||
const onLog = (d) => {
|
const onLog = (d) => {
|
||||||
const job = state.jobs.find((j) => j.jobId === jobId);
|
const job = state.jobs.find((j) => j.jobId === jobId);
|
||||||
job.log.push(d);
|
job.log.push(d);
|
||||||
job.status = jobStatus.ACTIVE;
|
job.status = jobStatus.ACTIVE;
|
||||||
|
@ -112,41 +115,48 @@ export const JobProvider = ({ children }) => {
|
||||||
};
|
};
|
||||||
const onPipelineTrigger = (trigger) => {
|
const onPipelineTrigger = (trigger) => {
|
||||||
console.log("Got trigger", trigger);
|
console.log("Got trigger", trigger);
|
||||||
}
|
};
|
||||||
const started = i.newPipelineJob(request, onLog, onClose, onPipelineTrigger);
|
const started = i.newPipelineJob(
|
||||||
started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId));
|
request,
|
||||||
|
onLog,
|
||||||
|
onClose,
|
||||||
|
onPipelineTrigger
|
||||||
|
);
|
||||||
|
started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId));
|
||||||
}
|
}
|
||||||
|
|
||||||
function pipelineFactory(builderCache) {
|
function pipelineFactory(builderCache) {
|
||||||
console.log("Would create pipeline with cache");
|
console.log("Would create pipeline with cache");
|
||||||
console.log(builderCache);
|
console.log(builderCache);
|
||||||
return pipelineComponentJob(state.pipelines, builderCache.branches[0][0]);
|
return pipelineComponentJob(state.pipelines, builderCache.branches[0][0]);
|
||||||
// return jobId;
|
// return jobId;
|
||||||
/*return jobFactory({testNames: ["primary"]});*/
|
/*return jobFactory({testNames: ["primary"]});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function jobCancel(jobId) {
|
||||||
|
const job = state.jobs.find((j) => j.jobId === jobId);
|
||||||
|
|
||||||
function jobCancel(jobId){
|
if (job.initiator.sk) job.initiator.sk.close();
|
||||||
const job = state.jobs.find((j)=>j.jobId===jobId);
|
|
||||||
|
|
||||||
if(job.initiator.sk) job.initiator.sk.close();
|
|
||||||
job.status = jobStatus.CANCELED;
|
job.status = jobStatus.CANCELED;
|
||||||
jobUpdate({ ...job }, jobId);
|
jobUpdate({ ...job }, jobId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function jobDestroy(jobId){
|
function jobDestroy(jobId) {
|
||||||
const job = state.jobs.find((j)=>j.jobId===jobId);
|
const job = state.jobs.find((j) => j.jobId === jobId);
|
||||||
|
|
||||||
if(job.initiator.sk && job.status !== jobStatus.OK && job.status !== jobStatus.ERROR && job.status !== jobStatus.CANCELED){
|
if (
|
||||||
job.initiator.sk.close();
|
job.initiator.sk &&
|
||||||
|
job.status !== jobStatus.OK &&
|
||||||
|
job.status !== jobStatus.ERROR &&
|
||||||
|
job.status !== jobStatus.CANCELED
|
||||||
|
) {
|
||||||
|
job.initiator.sk.close();
|
||||||
}
|
}
|
||||||
jobDelete(jobId);
|
jobDelete(jobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function jobFactory(builderCache) {
|
function jobFactory(builderCache) {
|
||||||
if(builderCache.tree) return pipelineFactory(builderCache);
|
if (builderCache.tree) return pipelineFactory(builderCache);
|
||||||
// Find test
|
// Find test
|
||||||
const i = new Initiator(url);
|
const i = new Initiator(url);
|
||||||
const jobId = `j${Date.now()}`;
|
const jobId = `j${Date.now()}`;
|
||||||
|
@ -197,7 +207,7 @@ export const JobProvider = ({ children }) => {
|
||||||
retryAll,
|
retryAll,
|
||||||
jobFactory,
|
jobFactory,
|
||||||
jobCancel,
|
jobCancel,
|
||||||
jobDestroy
|
jobDestroy,
|
||||||
};
|
};
|
||||||
const contextValue = useMemo(() => context, [state, dispatch]);
|
const contextValue = useMemo(() => context, [state, dispatch]);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useContext, useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import {useCurrentlyFailing} from "../Queries.jsx";
|
import { useCurrentlyFailing } from "../Queries.jsx";
|
||||||
import JobContext from "../ctx/JobContext.jsx";
|
import JobContext from "../ctx/JobContext.jsx";
|
||||||
import StoreContext from "../ctx/StoreContext.jsx";
|
import StoreContext from "../ctx/StoreContext.jsx";
|
||||||
|
|
||||||
|
@ -24,18 +24,22 @@ import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted";
|
||||||
import SettingsIcon from "@mui/icons-material/Settings";
|
import SettingsIcon from "@mui/icons-material/Settings";
|
||||||
import WarningIcon from "@mui/icons-material/Warning";
|
import WarningIcon from "@mui/icons-material/Warning";
|
||||||
import InfoIcon from "@mui/icons-material/Info";
|
import InfoIcon from "@mui/icons-material/Info";
|
||||||
import SentimentSatisfiedAltIcon from '@mui/icons-material/SentimentSatisfiedAlt';
|
import SentimentSatisfiedAltIcon from "@mui/icons-material/SentimentSatisfiedAlt";
|
||||||
|
|
||||||
const drawerWidth = 250;
|
const drawerWidth = 250;
|
||||||
|
|
||||||
export default function Navbar(props) {
|
export default function Navbar(props) {
|
||||||
const { state: jobState } = useContext(JobContext);
|
const { state: jobState } = useContext(JobContext);
|
||||||
const { state: store } = useContext(StoreContext);
|
const { state: store } = useContext(StoreContext);
|
||||||
const {isLoading, data: failing} = useCurrentlyFailing();
|
const { isLoading, data: failing } = useCurrentlyFailing();
|
||||||
const pages = store.pages;
|
const pages = store.pages;
|
||||||
const icons = [
|
const icons = [
|
||||||
<Badge badgeContent={(failing ?? []).length} color="error">
|
<Badge badgeContent={(failing ?? []).length} color="error">
|
||||||
{(failing ?? []).length > 0 ? <WarningIcon />: <SentimentSatisfiedAltIcon/>}
|
{(failing ?? []).length > 0 ? (
|
||||||
|
<WarningIcon />
|
||||||
|
) : (
|
||||||
|
<SentimentSatisfiedAltIcon />
|
||||||
|
)}
|
||||||
</Badge>,
|
</Badge>,
|
||||||
<NotificationsIcon />,
|
<NotificationsIcon />,
|
||||||
<Badge badgeContent={jobState.jobs.length} color="primary">
|
<Badge badgeContent={jobState.jobs.length} color="primary">
|
||||||
|
|
|
@ -33,7 +33,9 @@ export default function Views() {
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/"
|
path="/"
|
||||||
element={<Navigate to={`/qualiteer/${store.defaultPage}`} replace />}
|
element={
|
||||||
|
<Navigate to={`/qualiteer/${store.defaultPage}`} replace />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="/qualiteer/failing" element={<Failing />} />
|
<Route path="/qualiteer/failing" element={<Failing />} />
|
||||||
<Route path="/qualiteer/alerting" element={<Alerting />} />
|
<Route path="/qualiteer/alerting" element={<Alerting />} />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useContext } from "react";
|
import React, { useState, useContext } from "react";
|
||||||
import {useSilencedAlerts} from "../../Queries.jsx";
|
import { useSilencedAlerts } from "../../Queries.jsx";
|
||||||
import StoreContext from "../../ctx/StoreContext.jsx";
|
import StoreContext from "../../ctx/StoreContext.jsx";
|
||||||
import SilencedBox from "./SilencedBox.jsx";
|
import SilencedBox from "./SilencedBox.jsx";
|
||||||
import SilenceDialog from "./SilenceDialog.jsx";
|
import SilenceDialog from "./SilenceDialog.jsx";
|
||||||
|
@ -18,13 +18,9 @@ import Typography from "@mui/material/Typography";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
export default function Alerting() {
|
export default function Alerting() {
|
||||||
const {
|
const { updateStore, silenceRequest } = useContext(StoreContext);
|
||||||
updateStore,
|
|
||||||
silenceRequest,
|
|
||||||
} = useContext(StoreContext);
|
|
||||||
|
|
||||||
|
const { isLoading, data: silenced } = useSilencedAlerts();
|
||||||
const {isLoading, data: silenced} = useSilencedAlerts();
|
|
||||||
|
|
||||||
const [silenceEntry, setSilenceEntry] = useState({
|
const [silenceEntry, setSilenceEntry] = useState({
|
||||||
open: false,
|
open: false,
|
||||||
|
@ -61,35 +57,40 @@ const {isLoading, data: silenced} = useSilencedAlerts();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="alerting">
|
<div className="alerting">
|
||||||
{isLoading? null : silenced.map((v, i) => (
|
{isLoading
|
||||||
<SilencedBox
|
? null
|
||||||
key={i}
|
: silenced.map((v, i) => (
|
||||||
silenceEntry={v}
|
<SilencedBox
|
||||||
editSilence={editSilence(v)}
|
key={i}
|
||||||
removeSilence={removeSilence(v)}
|
silenceEntry={v}
|
||||||
/>
|
editSilence={editSilence(v)}
|
||||||
))}
|
removeSilence={removeSilence(v)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
{(silenced ?? []).length === 0? (
|
{(silenced ?? []).length === 0 ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
sx={{flexFlow: "wrap"}}
|
sx={{ flexFlow: "wrap" }}
|
||||||
>
|
>
|
||||||
|
<Typography variant="h4">No alerts silenced! </Typography>{" "}
|
||||||
<Typography variant="h4">No alerts silenced! </Typography> </Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
sx={{flexFlow: "wrap"}}
|
sx={{ flexFlow: "wrap" }}
|
||||||
> <Typography variant="h5">Click the '+' to create a new one!
|
>
|
||||||
</Typography>
|
{" "}
|
||||||
</Box>
|
<Typography variant="h5">
|
||||||
|
Click the '+' to create a new one!
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
): null}
|
) : null}
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
open={silenceEntry.deleteOpen}
|
open={silenceEntry.deleteOpen}
|
||||||
|
|
|
@ -3,13 +3,12 @@ import StoreContext from "../../ctx/StoreContext.jsx";
|
||||||
import JobContext from "../../ctx/JobContext.jsx";
|
import JobContext from "../../ctx/JobContext.jsx";
|
||||||
import CatalogBox from "./CatalogBox.jsx";
|
import CatalogBox from "./CatalogBox.jsx";
|
||||||
import CatalogSearch from "./CatalogSearch.jsx";
|
import CatalogSearch from "./CatalogSearch.jsx";
|
||||||
import {useCatalogTests} from "../../Queries.jsx";
|
import { useCatalogTests } from "../../Queries.jsx";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
|
|
||||||
export default function Catalog() {
|
export default function Catalog() {
|
||||||
|
|
||||||
const { state: store, updateStore } = useContext(StoreContext);
|
const { state: store, updateStore } = useContext(StoreContext);
|
||||||
const {isLoading, data: tests} = useCatalogTests();
|
const { isLoading, data: tests } = useCatalogTests();
|
||||||
const handleSearchChange = (e) =>
|
const handleSearchChange = (e) =>
|
||||||
updateStore({ catalogSearch: e.target.value });
|
updateStore({ catalogSearch: e.target.value });
|
||||||
|
|
||||||
|
@ -29,9 +28,9 @@ export default function Catalog() {
|
||||||
clearOnUnmount
|
clearOnUnmount
|
||||||
/>
|
/>
|
||||||
<h6>{store.catalogSearch}</h6>
|
<h6>{store.catalogSearch}</h6>
|
||||||
{isLoading ? null : tests.map((v, i) => (
|
{isLoading
|
||||||
<CatalogBox key={i} catalogTest={v} />
|
? null
|
||||||
))}
|
: tests.map((v, i) => <CatalogBox key={i} catalogTest={v} />)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ export default function CatalogBox(props) {
|
||||||
|
|
||||||
const { state: store, updateStore } = useContext(StoreContext);
|
const { state: store, updateStore } = useContext(StoreContext);
|
||||||
|
|
||||||
const { state: jobState, jobFactory} = useContext(JobContext);
|
const { state: jobState, jobFactory } = useContext(JobContext);
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ export default function CatalogBox(props) {
|
||||||
const runTest = (e) => {
|
const runTest = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const jobId = jobFactory({testNames: [testName]});
|
const jobId = jobFactory({ testNames: [testName] });
|
||||||
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useState, useContext } from "react";
|
import { useState, useContext } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import {useCurrentlyFailing, useSilencedAlerts} from "../../Queries.jsx";
|
import { useCurrentlyFailing, useSilencedAlerts } from "../../Queries.jsx";
|
||||||
import StoreContext from "../../ctx/StoreContext.jsx";
|
import StoreContext from "../../ctx/StoreContext.jsx";
|
||||||
import JobContext from "../../ctx/JobContext.jsx";
|
import JobContext from "../../ctx/JobContext.jsx";
|
||||||
import SilenceDialog from "../alerting/SilenceDialog.jsx";
|
import SilenceDialog from "../alerting/SilenceDialog.jsx";
|
||||||
|
@ -28,8 +28,8 @@ export default function Failing() {
|
||||||
updateStore,
|
updateStore,
|
||||||
silenceRequest,
|
silenceRequest,
|
||||||
} = useContext(StoreContext);
|
} = useContext(StoreContext);
|
||||||
const {isLoading, data: failing} = useCurrentlyFailing();
|
const { isLoading, data: failing } = useCurrentlyFailing();
|
||||||
const {isSilencedLoading, data:silencedAlerts} = useSilencedAlerts();
|
const { isSilencedLoading, data: silencedAlerts } = useSilencedAlerts();
|
||||||
const [silenceEntry, setSilenceEntry] = useState({ open: false });
|
const [silenceEntry, setSilenceEntry] = useState({ open: false });
|
||||||
|
|
||||||
const closeSilence = () => setSilenceEntry({ ...silenceEntry, open: false });
|
const closeSilence = () => setSilenceEntry({ ...silenceEntry, open: false });
|
||||||
|
@ -58,22 +58,28 @@ export default function Failing() {
|
||||||
|
|
||||||
const failingTestsWithJobs = () => {
|
const failingTestsWithJobs = () => {
|
||||||
const silences = silencedAlerts ?? [];
|
const silences = silencedAlerts ?? [];
|
||||||
for(var test of failing){
|
for (var test of failing) {
|
||||||
if(test.isCompound) continue;
|
if (test.isCompound) continue;
|
||||||
const job = jobState.jobs.find((j)=>j.builderCache.testNames.includes(test.name))
|
const job = jobState.jobs.find((j) =>
|
||||||
if(job) test.job = job;
|
j.builderCache.testNames.includes(test.name)
|
||||||
const silence = silences.find((s)=>s.name === test.name || s.class === test.class)
|
);
|
||||||
|
if (job) test.job = job;
|
||||||
|
const silence = silences.find(
|
||||||
|
(s) => s.name === test.name || s.class === test.class
|
||||||
|
);
|
||||||
|
|
||||||
if(silence) test.silencedUntil = silence;
|
if (silence) test.silencedUntil = silence;
|
||||||
}
|
}
|
||||||
return failing;
|
return failing;
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="failing">
|
<div className="failing">
|
||||||
{isLoading? null :failingTestsWithJobs().map((v, i) => (
|
{isLoading
|
||||||
<FailingBox key={i} failingTest={v} silenceClick={editSilence(v)} />
|
? null
|
||||||
))}
|
: failingTestsWithJobs().map((v, i) => (
|
||||||
|
<FailingBox key={i} failingTest={v} silenceClick={editSilence(v)} />
|
||||||
|
))}
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
open={retryAllOpen}
|
open={retryAllOpen}
|
||||||
|
@ -100,25 +106,21 @@ export default function Failing() {
|
||||||
onClose={handleSilenceClose}
|
onClose={handleSilenceClose}
|
||||||
silence={silenceEntry}
|
silence={silenceEntry}
|
||||||
/>
|
/>
|
||||||
{(failing ?? []).length === 0? ( <Box
|
{(failing ?? []).length === 0 ? (
|
||||||
display="flex"
|
<Box display="flex" alignItems="center" justifyContent="center">
|
||||||
alignItems="center"
|
<Typography variant="h4">No tests failing!</Typography>
|
||||||
justifyContent="center"
|
</Box>
|
||||||
>
|
) : null}
|
||||||
<Typography variant="h4">No tests failing!</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
|
{(failing ?? []).length === 0 ? null : (
|
||||||
): null}
|
<SpeedDial
|
||||||
|
ariaLabel="Retry All"
|
||||||
{(failing ?? []).length ===0? null :(
|
sx={{ position: "fixed", bottom: 16, right: 16 }}
|
||||||
<SpeedDial
|
icon={<ReplayIcon />}
|
||||||
ariaLabel="Retry All"
|
onClick={retryAllClick}
|
||||||
sx={{ position: "fixed", bottom: 16, right: 16 }}
|
open={false}
|
||||||
icon={<ReplayIcon />}
|
/>
|
||||||
onClick={retryAllClick}
|
)}
|
||||||
open={false}
|
|
||||||
/>)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ export default function FailingBox(props) {
|
||||||
failedMessage,
|
failedMessage,
|
||||||
isCompound,
|
isCompound,
|
||||||
jobStatus: testJobStatus,
|
jobStatus: testJobStatus,
|
||||||
job
|
job,
|
||||||
} = failingTest;
|
} = failingTest;
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
@ -78,12 +78,12 @@ export default function FailingBox(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const retryTest = () => {
|
const retryTest = () => {
|
||||||
const jobId = jobFactory({testNames: [testName], isTriage: true});
|
const jobId = jobFactory({ testNames: [testName], isTriage: true });
|
||||||
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const jobOnClick = () => {
|
const jobOnClick = () => {
|
||||||
if(!job) return retryTest;
|
if (!job) return retryTest;
|
||||||
switch (job.status) {
|
switch (job.status) {
|
||||||
case jobStatus.OK:
|
case jobStatus.OK:
|
||||||
return null;
|
return null;
|
||||||
|
@ -103,7 +103,7 @@ export default function FailingBox(props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function jobIcon() {
|
function jobIcon() {
|
||||||
if(!job) return <ReplayIcon />;
|
if (!job) return <ReplayIcon />;
|
||||||
switch (job.status) {
|
switch (job.status) {
|
||||||
case jobStatus.OK:
|
case jobStatus.OK:
|
||||||
return <CheckIcon color="success" />;
|
return <CheckIcon color="success" />;
|
||||||
|
|
|
@ -21,13 +21,11 @@ import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
|
||||||
import DeleteIcon from "@mui/icons-material/Delete";
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function JobView(props) {
|
export default function JobView(props) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { job } = props;
|
const { job } = props;
|
||||||
const { jobFactory, jobCancel, jobDestroy } = useContext(JobContext);
|
const { jobFactory, jobCancel, jobDestroy } = useContext(JobContext);
|
||||||
const {state: store} = useContext(StoreContext);
|
const { state: store } = useContext(StoreContext);
|
||||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const handleClick = (event) => {
|
const handleClick = (event) => {
|
||||||
|
@ -51,8 +49,8 @@ export default function JobView(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryJob() {
|
function retryJob() {
|
||||||
const jobId = jobFactory(job.builderCache);
|
const jobId = jobFactory(job.builderCache);
|
||||||
if(store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadLog() {
|
function downloadLog() {
|
||||||
|
@ -60,12 +58,11 @@ export default function JobView(props) {
|
||||||
download(`${job.jobId}.txt`, job.log.join("\n"));
|
download(`${job.jobId}.txt`, job.log.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelJob(){
|
function cancelJob() {
|
||||||
jobCancel(job.jobId);
|
jobCancel(job.jobId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteJob(){
|
function deleteJob() {
|
||||||
jobDestroy(job.jobId);
|
jobDestroy(job.jobId);
|
||||||
navigateToJobs();
|
navigateToJobs();
|
||||||
}
|
}
|
||||||
|
@ -107,7 +104,7 @@ export default function JobView(props) {
|
||||||
<Toolbar disableGutters />
|
<Toolbar disableGutters />
|
||||||
<JobLogView log={job.log} status={job.status} />
|
<JobLogView log={job.log} status={job.status} />
|
||||||
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
|
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
|
||||||
<MenuItem onClick={menuSelect(downloadLog)}>
|
<MenuItem onClick={menuSelect(downloadLog)}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<DownloadIcon fontSize="small" />
|
<DownloadIcon fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
@ -115,16 +112,27 @@ export default function JobView(props) {
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={menuSelect(retryJob)}>
|
<MenuItem onClick={menuSelect(retryJob)}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
{job.status === jobStatus.OK || job.status === jobStatus.ERROR ? <ReplayIcon fontSize="small" /> : <PlayArrowIcon fontSize="small"/>}
|
{job.status === jobStatus.OK || job.status === jobStatus.ERROR ? (
|
||||||
|
<ReplayIcon fontSize="small" />
|
||||||
|
) : (
|
||||||
|
<PlayArrowIcon fontSize="small" />
|
||||||
|
)}
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText> {job.status === jobStatus.ERROR? "Retry" : "Duplicate"}</ListItemText>
|
<ListItemText>
|
||||||
|
{" "}
|
||||||
|
{job.status === jobStatus.ERROR ? "Retry" : "Duplicate"}
|
||||||
|
</ListItemText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{job.status === jobStatus.OK || job.status === jobStatus.ERROR || job.status === jobStatus.CANCELED? null : <MenuItem onClick={menuSelect(cancelJob)}>
|
{job.status === jobStatus.OK ||
|
||||||
<ListItemIcon>
|
job.status === jobStatus.ERROR ||
|
||||||
<DoNotDisturbIcon fontSize="small" />
|
job.status === jobStatus.CANCELED ? null : (
|
||||||
</ListItemIcon>
|
<MenuItem onClick={menuSelect(cancelJob)}>
|
||||||
<ListItemText>Cancel</ListItemText>
|
<ListItemIcon>
|
||||||
</MenuItem>}
|
<DoNotDisturbIcon fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>Cancel</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
<MenuItem onClick={menuSelect(deleteJob)}>
|
<MenuItem onClick={menuSelect(deleteJob)}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<DeleteIcon fontSize="small" />
|
<DeleteIcon fontSize="small" />
|
||||||
|
|
|
@ -21,37 +21,42 @@ export default function Jobs() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="jobs">
|
<div className="jobs">
|
||||||
{jobState.jobs.length === 0? (
|
{jobState.jobs.length === 0 ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
sx={{flexFlow: "wrap"}}
|
sx={{ flexFlow: "wrap" }}
|
||||||
>
|
>
|
||||||
|
<Typography variant="h4">No jobs found! </Typography>{" "}
|
||||||
<Typography variant="h4">No jobs found! </Typography> </Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
sx={{flexFlow: "wrap"}}
|
sx={{ flexFlow: "wrap" }}
|
||||||
> <Typography variant="h5">Click the '+' to start a new one!
|
>
|
||||||
</Typography>
|
{" "}
|
||||||
</Box>
|
<Typography variant="h5">
|
||||||
|
Click the '+' to start a new one!
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
): null}
|
) : null}
|
||||||
<JobBuilder />
|
<JobBuilder />
|
||||||
{location.hash === "" &&
|
{location.hash === "" &&
|
||||||
jobState.jobs.filter((j)=>!j.isPipeline).map((v, i) => (
|
jobState.jobs
|
||||||
<a
|
.filter((j) => !j.isPipeline)
|
||||||
key={i}
|
.map((v, i) => (
|
||||||
href={`/qualiteer/jobs#${v.name}`}
|
<a
|
||||||
style={{ textDecoration: "none" }}
|
key={i}
|
||||||
>
|
href={`/qualiteer/jobs#${v.name}`}
|
||||||
<JobBox job={v} />
|
style={{ textDecoration: "none" }}
|
||||||
</a>
|
>
|
||||||
))}
|
<JobBox job={v} />
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
{jobState.jobs.find((job) => job.name === location.hash.slice(1)) && (
|
{jobState.jobs.find((job) => job.name === location.hash.slice(1)) && (
|
||||||
<JobView
|
<JobView
|
||||||
job={jobState.jobs.find((job) => job.name === location.hash.slice(1))}
|
job={jobState.jobs.find((job) => job.name === location.hash.slice(1))}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import DialogContent from "@mui/material/DialogContent";
|
||||||
function PipelineConfirm(props) {
|
function PipelineConfirm(props) {
|
||||||
const { cache, back, start } = props;
|
const { cache, back, start } = props;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import {usePipelineMappings} from "../../../Queries.jsx";
|
import { usePipelineMappings } from "../../../Queries.jsx";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import DialogActions from "@mui/material/DialogActions";
|
import DialogActions from "@mui/material/DialogActions";
|
||||||
import DialogContent from "@mui/material/DialogContent";
|
import DialogContent from "@mui/material/DialogContent";
|
||||||
|
@ -11,27 +11,27 @@ import Stack from "@mui/material/Stack";
|
||||||
|
|
||||||
function PipelineSelector(props) {
|
function PipelineSelector(props) {
|
||||||
const { cache, setCache, cancel, next } = props;
|
const { cache, setCache, cancel, next } = props;
|
||||||
const {isLoading, data: pipelineMappings} = usePipelineMappings();
|
const { isLoading, data: pipelineMappings } = usePipelineMappings();
|
||||||
|
|
||||||
const primaryMappings = () =>{
|
const primaryMappings = () => {
|
||||||
if(isLoading) return {};
|
if (isLoading) return {};
|
||||||
const primaryMappings = {};
|
const primaryMappings = {};
|
||||||
for (var pm of pipelineMappings) {
|
for (var pm of pipelineMappings) {
|
||||||
if (!(pm[0] in primaryMappings)) primaryMappings[pm[0]] = [];
|
if (!(pm[0] in primaryMappings)) primaryMappings[pm[0]] = [];
|
||||||
primaryMappings[pm[0]].push(pm);
|
primaryMappings[pm[0]].push(pm);
|
||||||
}
|
}
|
||||||
return primaryMappings;
|
return primaryMappings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const selectPrimary = (primarySelectedMappings) => () => {
|
const selectPrimary = (primarySelectedMappings) => () => {
|
||||||
setCache({ primarySelectedMappings });
|
setCache({ primarySelectedMappings });
|
||||||
};
|
};
|
||||||
|
|
||||||
const clickNext = () =>{
|
const clickNext = () => {
|
||||||
if(!cache.primarySelectedMappings ) return console.log("No mapping selected")
|
if (!cache.primarySelectedMappings)
|
||||||
next()
|
return console.log("No mapping selected");
|
||||||
}
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -53,11 +53,17 @@ function PipelineSelector(props) {
|
||||||
<Typography
|
<Typography
|
||||||
component={"span"}
|
component={"span"}
|
||||||
style={{ wordBreak: "break-word", margin: "auto 0" }}
|
style={{ wordBreak: "break-word", margin: "auto 0" }}
|
||||||
color={(cache.primarySelectedMappings ?? [[]])[0][0] ===k ? "primary": null}
|
color={
|
||||||
|
(cache.primarySelectedMappings ?? [[]])[0][0] === k
|
||||||
|
? "primary"
|
||||||
|
: null
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{k}
|
{k}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Stack sx={{ ml: "auto" }}>{(primaryMappings[k] ?? []).length}</Stack>
|
<Stack sx={{ ml: "auto" }}>
|
||||||
|
{(primaryMappings[k] ?? []).length}
|
||||||
|
</Stack>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -8,40 +8,48 @@ import AccordionSummary from "@mui/material/AccordionSummary";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import {selectBranch, asTree, asBranches, as1d} from "../../../../lib/jobs/pipelines.js";
|
import {
|
||||||
|
selectBranch,
|
||||||
|
asTree,
|
||||||
|
asBranches,
|
||||||
|
as1d,
|
||||||
|
} from "../../../../lib/jobs/pipelines.js";
|
||||||
|
|
||||||
function PipelineTrackSelector(props) {
|
function PipelineTrackSelector(props) {
|
||||||
const { cache, setCache, back, next } = props;
|
const { cache, setCache, back, next } = props;
|
||||||
const {primarySelectedMappings: primaries} = cache;
|
const { primarySelectedMappings: primaries } = cache;
|
||||||
const addTrack = (test) => {
|
const addTrack = (test) => {
|
||||||
const tracks = cache.tracks ?? []; tracks.push(selectBranch(primaries, test));
|
const tracks = cache.tracks ?? [];
|
||||||
setCache({ ...cache, tracks});
|
tracks.push(selectBranch(primaries, test));
|
||||||
|
setCache({ ...cache, tracks });
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeTrack = (test) => {
|
const removeTrack = (test) => {
|
||||||
const {tracks} = cache;
|
const { tracks } = cache;
|
||||||
for(var i in tracks){
|
for (var i in tracks) {
|
||||||
const index = tracks[i].indexOf(test);
|
const index = tracks[i].indexOf(test);
|
||||||
if(index===-1) continue;
|
if (index === -1) continue;
|
||||||
tracks[i] = tracks[i].slice(0,index);
|
tracks[i] = tracks[i].slice(0, index);
|
||||||
}
|
}
|
||||||
setCache({ ...cache, tracks});
|
setCache({ ...cache, tracks });
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectTrack = (test) => () => {
|
const selectTrack = (test) => () => {
|
||||||
if(as1d(cache.tracks).includes(test)) return removeTrack(test);
|
if (as1d(cache.tracks).includes(test)) return removeTrack(test);
|
||||||
addTrack(test);
|
addTrack(test);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getColor = (test) => as1d(cache.tracks).includes(test)
|
const getColor = (test) =>
|
||||||
? "primary"
|
as1d(cache.tracks).includes(test) ? "primary" : null;
|
||||||
: null;
|
|
||||||
|
|
||||||
const nextClick = () => {
|
const nextClick = () => {
|
||||||
|
setCache({
|
||||||
setCache({...cache, branches: asBranches(primaries), tree: asTree(cache.tracks)});
|
...cache,
|
||||||
|
branches: asBranches(primaries),
|
||||||
|
tree: asTree(cache.tracks),
|
||||||
|
});
|
||||||
next();
|
next();
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import primary from "./primary.js";
|
import primary from "./primary.js";
|
||||||
import secondary from "./secondary.js";
|
import secondary1 from "./secondary1.js";
|
||||||
import tertiary from "./tertiary.js";
|
import secondary2 from "./secondary2.js";
|
||||||
|
|
||||||
|
import tertiary1 from "./tertiary1.js";
|
||||||
|
import tertiary2 from "./tertiary2.js";
|
||||||
|
import tertiary3 from "./tertiary3.js";
|
||||||
|
|
||||||
import single from "./single.js";
|
import single from "./single.js";
|
||||||
import failing from "./failing.js";
|
import failing from "./failing.js";
|
||||||
|
|
||||||
|
@ -12,31 +17,33 @@ const reportingUrl = `${process.env.QUALITEER_URL}/api/dev/rabbit/TestResults`;
|
||||||
// Pull args
|
// Pull args
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const test = (args.find((v) => v.includes("test=")) ?? "").replace("test=", "");
|
const test = (args.find((v) => v.includes("test=")) ?? "").replace("test=", "");
|
||||||
const pipelineData = (
|
var pipeline = (args.find((v) => v.includes("pipeline=")) ?? "").replace(
|
||||||
args.find((v) => v.includes("pipelineData=")) ?? ""
|
"pipeline=",
|
||||||
).replace("pipelineData=", "");
|
""
|
||||||
const pipelineTriggers = (
|
);
|
||||||
args.find((v) => v.includes("pipelineTriggers=")) ?? ""
|
if (pipeline)
|
||||||
).replace("pipelineTriggers=", "");
|
pipeline = JSON.parse(Buffer.from(pipeline, "base64").toString("utf8"));
|
||||||
const pipelineDashboardSocket =
|
|
||||||
(args.find((v) => v.includes("pipelineDashboardSocket=")) ?? "").replace(
|
|
||||||
"pipelineDashboardSocket=",
|
|
||||||
""
|
|
||||||
) || undefined;
|
|
||||||
|
|
||||||
const logNow = () => console.log(Date.now());
|
const logNow = () => console.log(Date.now());
|
||||||
const liveIndicator = () => {
|
const liveIndicator = () => {
|
||||||
for (var i = 0; i < endLiveCount; i++) setTimeout(logNow, i * liveUpdateDelay);
|
for (var i = 0; i < endLiveCount; i++)
|
||||||
|
setTimeout(logNow, i * liveUpdateDelay);
|
||||||
};
|
};
|
||||||
|
|
||||||
const runTests = () => {
|
const runTests = () => {
|
||||||
switch (test) {
|
switch (test) {
|
||||||
case "primary":
|
case "primary":
|
||||||
return primary();
|
return primary();
|
||||||
case "secondary":
|
case "secondary1":
|
||||||
return secondary(pipelineData);
|
return secondary1(pipeline.testData);
|
||||||
case "tertiary":
|
case "secondary2":
|
||||||
return tertiary(pipelineData);
|
return secondary2(pipeline.testData);
|
||||||
|
case "tertiary1":
|
||||||
|
return tertiary1(pipeline.testData);
|
||||||
|
case "tertiary2":
|
||||||
|
return tertiary2(pipeline.testData);
|
||||||
|
case "tertiary3":
|
||||||
|
return tertiary3(pipeline.testData);
|
||||||
case "single":
|
case "single":
|
||||||
return single();
|
return single();
|
||||||
case "failing":
|
case "failing":
|
||||||
|
@ -50,15 +57,18 @@ const runTests = () => {
|
||||||
liveIndicator();
|
liveIndicator();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const status = runTests();
|
const status = runTests();
|
||||||
|
pipeline.testData = status.pipelineData;
|
||||||
const testResult = {
|
const testResult = {
|
||||||
...status,
|
...status,
|
||||||
name: test,
|
name: test,
|
||||||
pipelineTriggers: pipelineTriggers ? pipelineTriggers : undefined,
|
pipeline,
|
||||||
pipelineDashboardSocket,
|
|
||||||
};
|
};
|
||||||
axios.post(reportingUrl, { testResult }).catch((e) => {
|
axios
|
||||||
console.log(e.response.status);
|
.post(reportingUrl, { testResult })
|
||||||
}).then(()=>{
|
.catch((e) => {
|
||||||
if(status.status === 1) process.exit(1);
|
console.log(e.response.status);
|
||||||
});
|
})
|
||||||
|
.then(() => {
|
||||||
|
if (status.status === 1) process.exit(1);
|
||||||
|
});
|
||||||
}, endLiveCount * liveUpdateDelay);
|
}, endLiveCount * liveUpdateDelay);
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
export default function secondaryTest(pipelineData) {
|
|
||||||
console.log("This came from a secondary test!");
|
|
||||||
return { status: +(pipelineData !== "SomeData"), pipelineData: "SomeOtherData" };
|
|
||||||
}
|
|
7
tests/assets/suite/secondary1.js
Normal file
7
tests/assets/suite/secondary1.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export default function secondaryTest(pipelineData) {
|
||||||
|
console.log("This came from a secondary1 test!");
|
||||||
|
return {
|
||||||
|
status: +(pipelineData !== "SomeData"),
|
||||||
|
pipelineData: "SomeOtherData",
|
||||||
|
};
|
||||||
|
}
|
7
tests/assets/suite/secondary2.js
Normal file
7
tests/assets/suite/secondary2.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export default function secondaryTest(pipelineData) {
|
||||||
|
console.log("This came from a secondary2 test!");
|
||||||
|
return {
|
||||||
|
status: +(pipelineData !== "SomeData"),
|
||||||
|
pipelineData: "SomeOtherOtherData",
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export default function secondaryTest(pipelineData) {
|
export default function secondaryTest(pipelineData) {
|
||||||
console.log("This came from a tertiary!");
|
console.log("This came from a tertiary1!");
|
||||||
return { status: +(pipelineData !== "SomeOtherData") };
|
return { status: +(pipelineData !== "SomeOtherData") };
|
||||||
}
|
}
|
4
tests/assets/suite/tertiary2.js
Normal file
4
tests/assets/suite/tertiary2.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export default function secondaryTest(pipelineData) {
|
||||||
|
console.log("This came from a tertiary2!");
|
||||||
|
return { status: +(pipelineData !== "SomeOtherData") };
|
||||||
|
}
|
4
tests/assets/suite/tertiary3.js
Normal file
4
tests/assets/suite/tertiary3.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export default function secondaryTest(pipelineData) {
|
||||||
|
console.log("This came from a tertiary3!");
|
||||||
|
return { status: +(pipelineData !== "SomeOtherData") };
|
||||||
|
}
|
|
@ -14,7 +14,16 @@ const primary = new Initiator(url);
|
||||||
const job = {
|
const job = {
|
||||||
type: "compound",
|
type: "compound",
|
||||||
testName: "primary",
|
testName: "primary",
|
||||||
pipelineTriggers: "secondary",
|
pipeline: {
|
||||||
|
triggers: {
|
||||||
|
secondary1: {
|
||||||
|
tertiary1: {},
|
||||||
|
tertiary2: { __testDelay: 5000 },
|
||||||
|
__testDelay: 1000,
|
||||||
|
},
|
||||||
|
secondary2: { tertiary3: { __testDelay: 3000 }, __testDelay: 15000 },
|
||||||
|
},
|
||||||
|
},
|
||||||
name: "testing",
|
name: "testing",
|
||||||
image: "node",
|
image: "node",
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue