From 360ff368e1869ec50c0a3263f32fa85f8330f3ad Mon Sep 17 00:00:00 2001 From: Dunemask Date: Sat, 6 Aug 2022 21:21:41 +0000 Subject: [PATCH] More styling and mocking --- lib/database/queries/alerting.js | 15 +++- lib/database/queries/catalog.js | 88 ++++++++++++++++++++- lib/database/queries/results.js | 34 +++++++- lib/routes/alerting-route.js | 2 +- lib/routes/catalog-route.js | 11 ++- lib/routes/results-route.js | 2 +- package-lock.json | 76 ++++++++++++++++++ package.json | 1 + src/Dashboard.jsx | 5 ++ src/Queries.jsx | 27 +++++++ src/ctx/JobContext.jsx | 12 ++- src/ctx/StoreContext.jsx | 60 -------------- src/views/Navbar.jsx | 11 ++- src/views/alerting/Alerting.jsx | 33 +++++++- src/views/catalog/Catalog.jsx | 12 +-- src/views/catalog/CatalogBox.jsx | 4 +- src/views/failing/Failing.jsx | 23 ++++-- src/views/jobs/Jobs.jsx | 24 +++++- src/views/jobs/builder/PipelineConfirm.jsx | 1 + src/views/jobs/builder/PipelineSelector.jsx | 22 +++--- 20 files changed, 354 insertions(+), 109 deletions(-) create mode 100644 src/Queries.jsx diff --git a/lib/database/queries/alerting.js b/lib/database/queries/alerting.js index bcbf806..4bb0ce5 100644 --- a/lib/database/queries/alerting.js +++ b/lib/database/queries/alerting.js @@ -7,8 +7,21 @@ import { } from "../pg-query.js"; // Constants const table = "silenced_tests"; +const PG_DISABLED = process.env.POSTGRES_DISABLED; + +const silencedMock = () => { + return [{ + name: `single`, + class: `single.js`, + method: "FAKEMETHOD", + id: 0, + silencedUntil: new Date().toJSON(), + }] +}; + // Queries -export const getSilencedTests = () => { +export const getSilencedTests = async () => { + if (PG_DISABLED) return silencedMock(); const query = `SELECT * from ${table}`; return pg.query(query); }; diff --git a/lib/database/queries/catalog.js b/lib/database/queries/catalog.js index e0c27e1..b521a39 100644 --- a/lib/database/queries/catalog.js +++ b/lib/database/queries/catalog.js @@ -7,8 +7,94 @@ import { } from "../pg-query.js"; // Constants const table = "tests"; +const PG_DISABLED = process.env.POSTGRES_DISABLED; // Queries -export const getTests = () => { + +const testsMock = () => { + return [ + { + id: 0, + name: "single", + class: "single.js", + image: "node:latest", + isCompound: false, + type: "api", + description: "This is a single test", + tags: ["cron_1hour","reg_us", "env_ci", "proj_core", "skip_alt"], + path: "tests/assets/suite/single.js", + created: Date.now(), + mergeRequest: "https://example.com" + }, + { + id: 1, + name: "failing", + class: "failing.js", + image: "node:latest", + isCompound: false, + type: "ui", + description: "This is a failing test", + tags: ["cron_1hour","reg_us", "env_ci", "proj_core"], + path: "tests/assets/suite/failing.js", + created: Date.now(), + mergeRequest: "https://example.com" + }, + { + id: 2, + name: "primary", + class: "primary.js", + image: "node:latest", + isCompound: true, + type: "api", + description: "This is a primary test", + tags: ["cron_1hour","reg_us", "proj_core", "skip_alt", "compound_secondary"], + path: "tests/assets/suite/primary.js", + created: Date.now(), + mergeRequest: "https://example.com" + }, { + id: 3, + name: "secondary", + class: "secondary.js", + image: "node:latest", + isCompound: true, + type: "api", + description: "This is a secondary test", + tags: ["cron_1hour","reg_us", "proj_core", "compound_tertiary"], + path: "tests/assets/suite/secondary.js", + created: Date.now(), + mergeRequest: "https://example.com" + }, + { + id: 4, + name: "tertiary", + class: "tertiary.js", + image: "node:latest", + isCompound: true, + type: "api", + description: "This is a single test", + tags: ["cron_1hour","reg_us", "proj_core"], + path: "tests/assets/suite/tertiary.js", + created: Date.now(), + mergeRequest: "https://example.com" + }, + ]; +}; + +const mappingsMock = () => { + return [ + ["primary", "secondary1", "tertiary1"], + ["primary", "secondary1", "tertiary2"], + ["primary", "secondary2", "tertiary3"], +]; +} + +export const getTests = async () => { + if (PG_DISABLED) return testsMock(); + const query = `SELECT * from ${table}`; + return pg.query(query); +}; + +export const getPipelineMappings = async () => { + if (PG_DISABLED) return mappingsMock(); const query = `SELECT * from ${table}`; return pg.query(query); }; diff --git a/lib/database/queries/results.js b/lib/database/queries/results.js index 1cb0b99..9302c2e 100644 --- a/lib/database/queries/results.js +++ b/lib/database/queries/results.js @@ -8,6 +8,36 @@ import { } from "../pg-query.js"; // Constants const table = "test_results"; +const PG_DISABLED = process.env.POSTGRES_DISABLED; + + +const failingMock = () => { + return [{ + name: "failing", + class: "failing.js", + timestamp: new Date().toJSON(), + method: "FAKEMETHOD", + cron: "1hour", + type: "api", + dailyFails: 12, + screenshot: "https://picsum.photos/1920/1080", + recentResults: [1, 0, 0, 1, 0], + isCompound: false, + failedMessage: `Some Test FailureMessage`, +},{ + name: "secondary", + class: "secondary.js", + timestamp: new Date().toJSON(), + method: "FAKEMETHOD", + cron: "1hour", + type: "api", + dailyFails: 1, + screenshot: "https://picsum.photos/1920/1080", + recentResults: [1, 0, 0, 1, 0], + isCompound: true, + failedMessage: `Some Test FailureMessage from Secondary`, +}] +}; // Queries export const insertTestResult = (testResult) => { const { @@ -46,7 +76,9 @@ export const insertTestResult = (testResult) => { return pg.query(query); }; -export const getCurrentlyFailing = () => { +export const getCurrentlyFailing = async () => { + if (PG_DISABLED) return failingMock(); + /**/ const query = `WITH recent as (SELECT * FROM test_results WHERE (timestamp BETWEEN NOW() - INTERVAL '24 HOURS' AND NOW()) AND NOT(failed AND retry)) SELECT * FROM recent WHERE timestamp = (SELECT MAX(timestamp) FROM recent r2 WHERE recent.name = r2.name) AND failed; `; return pg.query(query); diff --git a/lib/routes/alerting-route.js b/lib/routes/alerting-route.js index 31d9b90..d111e08 100644 --- a/lib/routes/alerting-route.js +++ b/lib/routes/alerting-route.js @@ -7,7 +7,7 @@ router.use(jsonMiddleware()); // Get Routes router.get("/silenced", (req, res) => { - getSilencedTests().then(res.send); + getSilencedTests().then((t)=>res.send(t)); }); // Post Routes diff --git a/lib/routes/catalog-route.js b/lib/routes/catalog-route.js index a2f5b41..a9c58c3 100644 --- a/lib/routes/catalog-route.js +++ b/lib/routes/catalog-route.js @@ -1,5 +1,5 @@ import { Router, json as jsonMiddleware } from "express"; -import { getTests } from "../database/queries/catalog.js"; +import { getTests, getPipelineMappings } from "../database/queries/catalog.js"; const router = Router(); const maxSize = 1024 * 1024 * 100; // 100MB @@ -8,11 +8,14 @@ const maxSize = 1024 * 1024 * 100; // 100MB router.use(jsonMiddleware({ limit: maxSize })); // Get Routes -router.get("/tests", async (req, res) => { - const tests = await getTests(); - res.json(tests); +router.get("/tests", (req, res) => { + getTests().then((t)=>res.json(t)); }); +router.get("/pipeline-mappings", (req,res)=>{ + getPipelineMappings().then((m)=>res.json(m)); +}) + // Post Routes router.post("/update", (req, res) => { // Update All Tests diff --git a/lib/routes/results-route.js b/lib/routes/results-route.js index 025b8ff..b6efa70 100644 --- a/lib/routes/results-route.js +++ b/lib/routes/results-route.js @@ -7,7 +7,7 @@ router.use(jsonMiddleware()); // Get Routes router.get("/failing", (req, res) => { - res.send([]); + getCurrentlyFailing().then((f)=>res.json(f)); }); // Post Routes diff --git a/package-lock.json b/package-lock.json index ec33c0e..da418ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "LGPL-2.1-only", "dependencies": { + "@tanstack/react-query": "^4.0.0", "amqplib": "^0.8.0", "chalk": "^5.0.1", "dotenv": "^16.0.0", @@ -1071,6 +1072,42 @@ "node": ">=6" } }, + "node_modules/@tanstack/query-core": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.0.10.tgz", + "integrity": "sha512-9LsABpZXkWZHi4P1ozRETEDXQocLAxVzQaIhganxbNuz/uA3PsCAJxJTiQrknG5htLMzOF5MqM9G10e6DCxV1A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.0.10.tgz", + "integrity": "sha512-Wn5QhZUE5wvr6rGClV7KeQIUsdTmYR9mgmMZen7DSRWauHW2UTynFg3Kkf6pw+XlxxOLsyLWwz/Q6q1lSpM3TQ==", + "dependencies": { + "@tanstack/query-core": "^4.0.0-beta.1", + "@types/use-sync-external-store": "^0.0.3", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/@types/component-emitter": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", @@ -1153,6 +1190,11 @@ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "dev": true }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@vitejs/plugin-react": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.1.1.tgz", @@ -5646,6 +5688,14 @@ "node": ">=4" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -6585,6 +6635,21 @@ "defer-to-connect": "^1.0.1" } }, + "@tanstack/query-core": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.0.10.tgz", + "integrity": "sha512-9LsABpZXkWZHi4P1ozRETEDXQocLAxVzQaIhganxbNuz/uA3PsCAJxJTiQrknG5htLMzOF5MqM9G10e6DCxV1A==" + }, + "@tanstack/react-query": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.0.10.tgz", + "integrity": "sha512-Wn5QhZUE5wvr6rGClV7KeQIUsdTmYR9mgmMZen7DSRWauHW2UTynFg3Kkf6pw+XlxxOLsyLWwz/Q6q1lSpM3TQ==", + "requires": { + "@tanstack/query-core": "^4.0.0-beta.1", + "@types/use-sync-external-store": "^0.0.3", + "use-sync-external-store": "^1.2.0" + } + }, "@types/component-emitter": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", @@ -6667,6 +6732,11 @@ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "dev": true }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "@vitejs/plugin-react": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.1.1.tgz", @@ -10052,6 +10122,12 @@ "prepend-http": "^2.0.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", diff --git a/package.json b/package.json index 51750e6..5c5073a 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "path": "^0.12.7", "pg-promise": "^10.11.1", "postgres-migrations": "^5.3.0", + "@tanstack/react-query": "^4.0.0", "react-router-dom": "^6.3.0", "socket.io": "^4.4.1", "socket.io-client": "^4.4.1", diff --git a/src/Dashboard.jsx b/src/Dashboard.jsx index 43d679b..b7a1d09 100644 --- a/src/Dashboard.jsx +++ b/src/Dashboard.jsx @@ -1,13 +1,17 @@ // Import Contexts +import { QueryClient, QueryClientProvider} from '@tanstack/react-query' import { JobProvider } from "./ctx/JobContext.jsx"; import { StoreProvider } from "./ctx/StoreContext.jsx"; import { BrowserRouter } from "react-router-dom"; // Import Views import Views from "./views/Views.jsx"; +const queryClient = new QueryClient() + export default function Dashboard() { return (
+ @@ -15,6 +19,7 @@ export default function Dashboard() { +
); } diff --git a/src/Queries.jsx b/src/Queries.jsx new file mode 100644 index 0000000..1856066 --- /dev/null +++ b/src/Queries.jsx @@ -0,0 +1,27 @@ +import { useQuery } from '@tanstack/react-query' + +const QUALITEER_URL = "https://qualiteer.elijahparker3.repl.co/api"; + +export const useCatalogTests = () => useQuery(['catalogTests'], () => + fetch(`${QUALITEER_URL}/catalog/tests`).then(res => + res.json() + ) + ) + +export const usePipelineMappings = () => useQuery(['pipelineMappings'], () => + fetch(`${QUALITEER_URL}/catalog/pipeline-mappings`).then(res => + res.json() + ) + ) + +export const useSilencedAlerts = () => useQuery(['silenced'], () => + fetch(`${QUALITEER_URL}/alerting/silenced`).then(res => + res.json() + ) + ) + +export const useCurrentlyFailing = () => useQuery(['failing'], () => + fetch(`${QUALITEER_URL}/results/failing`).then(res => + res.json() + ) + ) \ No newline at end of file diff --git a/src/ctx/JobContext.jsx b/src/ctx/JobContext.jsx index 471d9db..b84cc76 100644 --- a/src/ctx/JobContext.jsx +++ b/src/ctx/JobContext.jsx @@ -72,15 +72,14 @@ export const JobProvider = ({ children }) => { return jobFactory({ testNames: ["single"] }); } - function jobBuilder(tests) { - if (!Array.isArray(tests)) throw Error("Error from within JobContext.jsx"); - console.log("Would run tests", tests); - return jobFactory({ testNames: ["single"] }); + function pipelineFactory(builderCache) { + console.log("Would create pipeline with cache"); + console.log(builderCache); + return jobFactory({testNames: ["single"]}); } - function pipelineFactory(builderCache) {} - function jobFactory(builderCache) { + if(builderCache.pipelineMasterTrack) return pipelineFactory(builderCache); // Find test const i = new Initiator(url); const jobId = `j${Date.now()}`; @@ -141,7 +140,6 @@ export const JobProvider = ({ children }) => { jobDelete, retryAll, retrySingle, - jobBuilder, jobFactory, }; const contextValue = useMemo(() => context, [state, dispatch]); diff --git a/src/ctx/StoreContext.jsx b/src/ctx/StoreContext.jsx index 60e2cad..974f295 100644 --- a/src/ctx/StoreContext.jsx +++ b/src/ctx/StoreContext.jsx @@ -1,66 +1,10 @@ import React, { useReducer, createContext, useMemo } from "react"; - const StoreContext = createContext(); const ACTIONS = { UPDATE: "u", }; -const pipelineMappingsMock = [ - ["primary", "secondary1", "tertiary1"], - ["primary", "secondary1", "tertiary2"], - ["primary", "secondary2", "tertiary3"], -]; - -const silencedMock = new Array(10).fill(0).map((v, i) => ({ - name: `Test${i + 1}`, - class: `SomeTestClass${i % 2 ? i - 1 : i / 2}`, - method: "someMethod", - id: Date.now(), - silencedUntil: `2022-05-10T16:${2 + i}:33.810Z`, -})); - -const catalogMock = new Array(10).fill(0).map((v, i) => ({ - name: `Test${i + 1}`, - class: `SomeTestClass${i % 2 ? i - 1 : i / 2}`, - repo: "Repo", - isCompound: i % 5 ? false : true, - type: i % 3 ? "api" : "ui", -})); - -const failingMock = new Array(12).fill(0).map((v, i) => ({ - class: `SomeTestClass${i % 2 ? i - 1 : i / 2}`, - name: `TestThatDoesOneThing${i + 1}`, - timestamp: `2022-05-10T16:0${i < 10 ? i : i - 10}:33.810Z`, - method: `SomeMethod`, - silencedUntil: i % 4 ? null : `2022-05-10T16:0${i}:33.810Z`, - frequency: "1hour", - type: i % 3 ? "api" : "ui", - dailyFails: i + 1, - screenshot: "https://picsum.photos/1920/1080", - recentResults: [1, 0, 0, 1, 0], - isCompound: i % 5 ? false : true, - failedMessage: `Some Test FailureMessage ${i}`, - jobStatus: (() => { - switch (i) { - case 1: - return "o"; - case 3: - return "e"; - case 4: - return "p"; - case 5: - return "a"; - case 6: - return "c"; - case 8: - return "q"; - default: - return null; - } - })(), -})); - const localStorage = { setItem: () => {}, getItem: () => {} }; const localSettings = localStorage.getItem("settings"); @@ -78,10 +22,6 @@ const settingsKeys = Object.keys(defaultSettings); const initialState = { pages: ["failing", "alerting", "jobs", "catalog", "settings", "about"], intervals: [], - catalog: catalogMock, - failing: failingMock, - silenced: silencedMock, - pipelineMappings: pipelineMappingsMock, regions: [], catalogSearch: "", ...settings, diff --git a/src/views/Navbar.jsx b/src/views/Navbar.jsx index 5d1f568..842171d 100644 --- a/src/views/Navbar.jsx +++ b/src/views/Navbar.jsx @@ -1,6 +1,7 @@ import { useContext, useState } from "react"; -import StoreContext from "../ctx/StoreContext.jsx"; +import {useCurrentlyFailing} from "../Queries.jsx"; import JobContext from "../ctx/JobContext.jsx"; +import StoreContext from "../ctx/StoreContext.jsx"; import { Link, useLocation } from "react-router-dom"; import AppBar from "@mui/material/AppBar"; @@ -23,16 +24,18 @@ import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted"; import SettingsIcon from "@mui/icons-material/Settings"; import WarningIcon from "@mui/icons-material/Warning"; import InfoIcon from "@mui/icons-material/Info"; +import SentimentSatisfiedAltIcon from '@mui/icons-material/SentimentSatisfiedAlt'; const drawerWidth = 250; export default function Navbar(props) { const { state: jobState } = useContext(JobContext); const { state: store } = useContext(StoreContext); + const {isLoading, data: failing} = useCurrentlyFailing(); const pages = store.pages; const icons = [ - - + + {(failing ?? []).length > 0 ? : } , , @@ -66,7 +69,7 @@ export default function Navbar(props) { if (location.pathname !== "/qualiteer/failing") return pathStr; return ( diff --git a/src/views/alerting/Alerting.jsx b/src/views/alerting/Alerting.jsx index d2de54a..54c58fd 100644 --- a/src/views/alerting/Alerting.jsx +++ b/src/views/alerting/Alerting.jsx @@ -1,4 +1,5 @@ -import { useState, useContext } from "react"; +import React, { useState, useContext } from "react"; +import {useSilencedAlerts} from "../../Queries.jsx"; import StoreContext from "../../ctx/StoreContext.jsx"; import SilencedBox from "./SilencedBox.jsx"; import SilenceDialog from "./SilenceDialog.jsx"; @@ -13,14 +14,18 @@ import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; import DialogContentText from "@mui/material/DialogContentText"; import DialogTitle from "@mui/material/DialogTitle"; +import Typography from "@mui/material/Typography"; +import Box from "@mui/material/Box"; export default function Alerting() { const { - state: store, updateStore, silenceRequest, } = useContext(StoreContext); + +const {isLoading, data: silenced} = useSilencedAlerts(); + const [silenceEntry, setSilenceEntry] = useState({ open: false, deleteOpen: false, @@ -56,7 +61,7 @@ export default function Alerting() { return (
- {store.silenced.map((v, i) => ( + {isLoading? null : silenced.map((v, i) => ( ))} + + {(silenced ?? []).length === 0? ( + + + + No alerts silenced! + Click the '+' to create a new one! + + + + ): null} + updateStore({ catalogSearch: e.target.value }); @@ -35,7 +29,7 @@ export default function Catalog() { clearOnUnmount />
{store.catalogSearch}
- {store.catalog.map((v, i) => ( + {isLoading ? null : tests.map((v, i) => ( ))}
diff --git a/src/views/catalog/CatalogBox.jsx b/src/views/catalog/CatalogBox.jsx index 74de3f6..b8bca28 100644 --- a/src/views/catalog/CatalogBox.jsx +++ b/src/views/catalog/CatalogBox.jsx @@ -29,7 +29,7 @@ export default function CatalogBox(props) { const { state: store, updateStore } = useContext(StoreContext); - const { state: jobState, jobBuilder } = useContext(JobContext); + const { state: jobState, jobFactory} = useContext(JobContext); const [open, setOpen] = useState(false); @@ -38,7 +38,7 @@ export default function CatalogBox(props) { const runTest = (e) => { e.preventDefault(); e.stopPropagation(); - const jobId = jobBuilder([catalogTest]); + const jobId = jobFactory({testNames: [testName]}); if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`); }; diff --git a/src/views/failing/Failing.jsx b/src/views/failing/Failing.jsx index 58d51e7..e737e75 100644 --- a/src/views/failing/Failing.jsx +++ b/src/views/failing/Failing.jsx @@ -1,13 +1,12 @@ import { useState, useContext } from "react"; import { useNavigate } from "react-router-dom"; +import {useCurrentlyFailing} from "../../Queries.jsx"; import StoreContext from "../../ctx/StoreContext.jsx"; import JobContext from "../../ctx/JobContext.jsx"; import SilenceDialog from "../alerting/SilenceDialog.jsx"; import FailingBox from "./FailingBox.jsx"; import SpeedDial from "@mui/material/SpeedDial"; -import SpeedDialAction from "@mui/material/SpeedDialAction"; -import SpeedDialIcon from "@mui/material/SpeedDialIcon"; import Button from "@mui/material/Button"; import Dialog from "@mui/material/Dialog"; @@ -17,6 +16,9 @@ import DialogContentText from "@mui/material/DialogContentText"; import DialogTitle from "@mui/material/DialogTitle"; import ReplayIcon from "@mui/icons-material/Replay"; +import Box from "@mui/material/Box"; + +import Typography from "@mui/material/Typography"; export default function Failing() { const { state: jobState, retryAll } = useContext(JobContext); @@ -26,7 +28,7 @@ export default function Failing() { updateStore, silenceRequest, } = useContext(StoreContext); - const { failing } = store; + const {isLoading, data: failing} = useCurrentlyFailing(); const [silenceEntry, setSilenceEntry] = useState({ open: false }); @@ -56,7 +58,7 @@ export default function Failing() { return (
- {failing.map((v, i) => ( + {isLoading? null :failing.map((v, i) => ( ))} @@ -85,13 +87,24 @@ export default function Failing() { onClose={handleSilenceClose} silence={silenceEntry} /> + {(failing ?? []).length === 0? ( + No tests failing! + + + + ): null} + {(failing ?? []).length ===0? null :( } onClick={retryAllClick} open={false} - /> + />)}
); } diff --git a/src/views/jobs/Jobs.jsx b/src/views/jobs/Jobs.jsx index c6833c3..5e38600 100644 --- a/src/views/jobs/Jobs.jsx +++ b/src/views/jobs/Jobs.jsx @@ -1,10 +1,12 @@ -import { useState, useContext, useEffect } from "react"; +import React, { useState, useContext, useEffect } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import JobContext from "../../ctx/JobContext.jsx"; import JobBox from "./JobBox.jsx"; import JobView from "./JobView.jsx"; import JobBuilder from "./builder/JobBuilder.jsx"; +import Typography from "@mui/material/Typography"; +import Box from "@mui/material/Box"; export default function Jobs() { const { state: jobState } = useContext(JobContext); @@ -19,6 +21,26 @@ export default function Jobs() { return (
+ {jobState.jobs.length === 0? ( + + + + No jobs found! + Click the '+' to start a new one! + + + + ): null} {location.hash === "" && jobState.jobs.map((v, i) => ( diff --git a/src/views/jobs/builder/PipelineConfirm.jsx b/src/views/jobs/builder/PipelineConfirm.jsx index 48a6adb..c120861 100644 --- a/src/views/jobs/builder/PipelineConfirm.jsx +++ b/src/views/jobs/builder/PipelineConfirm.jsx @@ -6,6 +6,7 @@ import DialogContent from "@mui/material/DialogContent"; function PipelineConfirm(props) { const { cache, back, start } = props; + return ( diff --git a/src/views/jobs/builder/PipelineSelector.jsx b/src/views/jobs/builder/PipelineSelector.jsx index b893cca..b2a9d0e 100644 --- a/src/views/jobs/builder/PipelineSelector.jsx +++ b/src/views/jobs/builder/PipelineSelector.jsx @@ -1,6 +1,5 @@ import React, { useContext } from "react"; -import StoreContext from "../../../ctx/StoreContext.jsx"; - +import {usePipelineMappings} from "../../../Queries.jsx"; import Button from "@mui/material/Button"; import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; @@ -12,14 +11,19 @@ import Stack from "@mui/material/Stack"; function PipelineSelector(props) { const { cache, setCache, cancel, next } = props; - const { state: store } = useContext(StoreContext); - const { pipelineMappings } = store; - const primaryMappings = {}; + const {isLoading, data: pipelineMappings} = usePipelineMappings(); + + const primaryMappings = () =>{ + if(isLoading) return {}; + const primaryMappings = {}; for (var pm of pipelineMappings) { if (!(pm[0] in primaryMappings)) primaryMappings[pm[0]] = []; primaryMappings[pm[0]].push(pm); } - + return primaryMappings; + }; + + const selectPrimary = (primarySelectedMappings) => () => { setCache({ primarySelectedMappings }); }; @@ -27,13 +31,13 @@ function PipelineSelector(props) { return ( - {Object.keys(primaryMappings).map((k, i) => ( + {Object.keys(primaryMappings()).map((k, i) => ( {k} - {primaryMappings[k].length} + {(primaryMappings[k] ?? []).length} ))}