diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml index 425f72538ec..e69de29bb2d 100644 --- a/.github/workflows/autoupdate.yml +++ b/.github/workflows/autoupdate.yml @@ -1,22 +0,0 @@ -name: autoupdate -on: - # This will trigger on all pushes to all branches. - push: {} - # Alternatively, you can only trigger if commits are pushed to certain branches, e.g.: - # push: - # branches: - # - master - # - unstable -jobs: - autoupdate: - name: autoupdate - runs-on: ubuntu-22.04 - steps: - - uses: docker://chinthakagodawita/autoupdate-action:v1 - env: - GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' - PR_LABELS: "auto-update" - MERGE_MSG: "Branch was auto-updated." - RETRY_COUNT: "5" - RETRY_SLEEP: "300" - MERGE_CONFLICT_ACTION: "fail" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b688b3731b..2ee45d73f99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,13 +27,15 @@ permissions: jobs: lint: + if: github.event_name == 'schedule' || github.event_name == 'push' || github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id uses: ./.github/workflows/lint.yml test: + if: github.event_name == 'schedule' || github.event_name == 'push' || github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id uses: ./.github/workflows/test.yml # Virtual job that can be configured as a required check before a PR can be merged. all-required-checks-done: name: All required checks done - if: ${{ always() }} + if: github.event_name == 'schedule' || github.event_name == 'push' || github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id needs: - lint - test diff --git a/cmd/testsuite/cmd/test.go b/cmd/testsuite/cmd/test.go index 72eef04d966..ccf8ecdf9fd 100644 --- a/cmd/testsuite/cmd/test.go +++ b/cmd/testsuite/cmd/test.go @@ -31,6 +31,8 @@ func testCmd(app *testsuite.App) *cobra.Command { cmd.Flags().String("tests", "", "Test file pattern, e.g., './testcases/*.yaml'.") cmd.Flags().String("junit", "", "Write a JUnit test report to this path.") cmd.Flags().String("benchmark", "", "Write a benchmark test report to this path.") + cmd.Flags().String("prometheusPushgatewayUrl", "", "Push metrics to Prometheus pushgateway at this url.") + cmd.Flags().String("prometheusPushgatewayJobName", "armada-testsuite", "Metrics are annotated with with job=prometheusPushGatewayJobName.") return cmd } @@ -64,6 +66,18 @@ func testCmdRunE(app *testsuite.App) func(cmd *cobra.Command, args []string) err return errors.New("benchmark report not currently supported") } + prometheusPushgatewayUrl, err := cmd.Flags().GetString("prometheusPushgatewayUrl") + if err != nil { + return errors.WithStack(err) + } + app.Params.PrometheusPushGatewayUrl = prometheusPushgatewayUrl + + prometheusPushgatewayJobName, err := cmd.Flags().GetString("prometheusPushgatewayJobName") + if err != nil { + return errors.WithStack(err) + } + app.Params.PrometheusPushGatewayJobName = prometheusPushgatewayJobName + // Create a context that is cancelled on SIGINT/SIGTERM. // Ensures test jobs are cancelled on ctrl-C. ctx, cancel := context.WithCancel(context.Background()) diff --git a/internal/lookout/ui/.eslintrc.js b/internal/lookout/ui/.eslintrc.js index 81f176d2068..133e26f10cd 100644 --- a/internal/lookout/ui/.eslintrc.js +++ b/internal/lookout/ui/.eslintrc.js @@ -12,7 +12,7 @@ module.exports = { version: "detect", }, }, - extends: ["plugin:react/recommended", "plugin:@typescript-eslint/recommended", "prettier"], + extends: ["plugin:@typescript-eslint/recommended", "prettier"], plugins: ["prettier", "eslint-plugin-import"], rules: { "prettier/prettier": [ diff --git a/internal/lookout/ui/README.md b/internal/lookout/ui/README.md index ecf2e9e6b90..09d713756d6 100644 --- a/internal/lookout/ui/README.md +++ b/internal/lookout/ui/README.md @@ -6,6 +6,12 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo In the project directory, you can run: +### `yarn openapi` + +This step requires Docker. + +Generate the OpenAPI client code from the OpenAPI specification. This step is required to be run before the first time the application is run, and any time the OpenAPI specification is updated. + ### `yarn start` Runs the app in the development mode.\ diff --git a/internal/lookout/ui/package.json b/internal/lookout/ui/package.json index 6e637fb16c9..26d22c756c6 100644 --- a/internal/lookout/ui/package.json +++ b/internal/lookout/ui/package.json @@ -19,13 +19,13 @@ }, "dependencies": { "@emotion/react": "^11.10.5", - "@emotion/styled": "^11.10.5", + "@emotion/styled": "^11.11.0", "@material-ui/core": "^4.11.4", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.58", "@mui/icons-material": "^5.14.3", "@mui/lab": "^5.0.0-alpha.111", - "@mui/material": "^5.13.6", + "@mui/material": "^5.14.4", "@tanstack/react-table": "^8.7.0", "@testing-library/jest-dom": "^5.11.5", "@testing-library/react": "^12.1.5", @@ -34,7 +34,7 @@ "@types/js-yaml": "^4.0.0", "@types/node": "^12.19.3", "@types/react": "^16.14.43", - "@types/react-dom": "^16.9.9", + "@types/react-dom": "^16.9.19", "@types/react-virtualized": "^9.21.10", "@types/uuid": "^8.3.0", "@typescript-eslint/eslint-plugin": "^5.61.0", @@ -48,7 +48,7 @@ "eslint-config-prettier": "^8.10.0", "eslint-plugin-import": "^2.28.0", "eslint-plugin-prettier": "^3.4.0", - "eslint-plugin-react": "^7.31.11", + "eslint-plugin-react": "^7.33.1", "jest-junit": "^16.0.0", "js-yaml": "^4.0.0", "notistack": "^2.0.8", @@ -57,7 +57,7 @@ "query-string": "^6.13.7", "react": "^17.0.1", "react-dom": "^17.0.1", - "react-router-dom": "6.14.1", + "react-router-dom": "6.14.2", "react-scripts": "^5.0.1", "react-truncate": "^2.4.0", "react-virtualized": "^9.22.2", diff --git a/internal/lookout/ui/src/App.css b/internal/lookout/ui/src/App.css index 5abcd5a03aa..65f2d14bb82 100644 --- a/internal/lookout/ui/src/App.css +++ b/internal/lookout/ui/src/App.css @@ -6,6 +6,7 @@ .app-content { height: 100%; + min-height: 0; display: flex; flex-direction: column; } diff --git a/internal/lookout/ui/src/components/lookoutV2/JobsTableCell.tsx b/internal/lookout/ui/src/components/lookoutV2/JobsTableCell.tsx index 34c60fb0e31..0d4e4f64b69 100644 --- a/internal/lookout/ui/src/components/lookoutV2/JobsTableCell.tsx +++ b/internal/lookout/ui/src/components/lookoutV2/JobsTableCell.tsx @@ -11,11 +11,9 @@ import { matchForColumn } from "../../utils/jobsTableUtils" import styles from "./JobsTableCell.module.css" import { JobsTableFilter } from "./JobsTableFilter" + const sharedCellStyle = { padding: 0, - "&:hover": { - opacity: 0.85, - }, overflowWrap: "normal", textOverflow: "ellipsis", whiteSpace: "nowrap", @@ -23,6 +21,13 @@ const sharedCellStyle = { borderRight: "1px solid #cccccc", } +const sharedCellStyleWithOpacity = { + ...sharedCellStyle, + "&:hover": { + opacity: 0.85, + }, +} + export interface HeaderCellProps { header: Header columnResizeMode: ColumnResizeMode @@ -70,6 +75,7 @@ export function HeaderCell({ textOverflow: "ellipsis", whiteSpace: "nowrap", overflow: "hidden", + backgroundColor: "#f2f2f2", }} className={styles.headerCell} /> @@ -100,6 +106,7 @@ export function HeaderCell({ justifyContent: "space-between", alignItems: "center", margin: 0, + backgroundColor: "#f2f2f2", }} >
diff --git a/internal/lookout/ui/src/containers/lookoutV2/JobsTableContainer.test.tsx b/internal/lookout/ui/src/containers/lookoutV2/JobsTableContainer.test.tsx index 5611485d67e..02fa46af10c 100644 --- a/internal/lookout/ui/src/containers/lookoutV2/JobsTableContainer.test.tsx +++ b/internal/lookout/ui/src/containers/lookoutV2/JobsTableContainer.test.tsx @@ -401,7 +401,7 @@ describe("JobsTableContainer", () => { await waitFor(() => { const rows = getAllByRole("row") // Order should be reversed now - expect(rows[rows.length - 2]).toHaveTextContent(sorted[0].jobId) + expect(rows[rows.length - 1]).toHaveTextContent(sorted[0].jobId) }) }) }) @@ -561,7 +561,7 @@ describe("JobsTableContainer", () => { async () => { const table = await screen.findByRole("table", { name: "Jobs table" }) const rows = await within(table).findAllByRole("row") - expect(rows.length).toBe(nDataRows + 2) // One row per data row, plus the header and footer rows + expect(rows.length).toBe(nDataRows + 1) // One row per data row, plus the header }, { timeout: 3000 }, ) diff --git a/internal/lookout/ui/src/containers/lookoutV2/JobsTableContainer.tsx b/internal/lookout/ui/src/containers/lookoutV2/JobsTableContainer.tsx index 2738c32d62c..b16a51b09fa 100644 --- a/internal/lookout/ui/src/containers/lookoutV2/JobsTableContainer.tsx +++ b/internal/lookout/ui/src/containers/lookoutV2/JobsTableContainer.tsx @@ -9,7 +9,6 @@ import { TableBody, TableCell, TableContainer, - TableFooter, TableHead, TablePagination, TableRow, @@ -668,95 +667,96 @@ export const JobsTableContainer = ({ columnsForSelect = columnsForSelect.filter((col) => col.id !== StandardColumnId.Count) } return ( - - - 0} - allColumns={columnsForSelect} - groupedColumns={grouping} - visibleColumns={visibleColumnIds} - selectedItemFilters={selectedItemsFilters} - customViews={customViews} - activeJobSets={activeJobSets} - onActiveJobSetsChanged={(newVal) => { - setActiveJobSets(newVal) - onRefresh() - }} - onRefresh={onRefresh} - onAddAnnotationColumn={addAnnotationCol} - onRemoveAnnotationColumn={removeAnnotationCol} - onEditAnnotationColumn={editAnnotationCol} - onGroupsChanged={onGroupingChange} - toggleColumnVisibility={onColumnVisibilityChange} - getJobsService={getJobsService} - updateJobsService={updateJobsService} - onClearFilters={clearFilters} - onAddCustomView={addCustomView} - onDeleteCustomView={deleteCustomView} - onLoadCustomView={loadCustomView} - /> - - + + + 0} + allColumns={columnsForSelect} + groupedColumns={grouping} + visibleColumns={visibleColumnIds} + selectedItemFilters={selectedItemsFilters} + customViews={customViews} + activeJobSets={activeJobSets} + onActiveJobSetsChanged={(newVal) => { + setActiveJobSets(newVal) + onRefresh() }} - > - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - { - setTextFieldRef(header.id, ref) - }} - /> - ))} - - ))} - - - 0} - columns={table.getVisibleLeafColumns()} - topLevelRows={topLevelRows} - sidebarJobId={sidebarJobId} - onLoadMoreSubRows={onLoadMoreSubRows} - onClickRowCheckbox={(row) => selectRow(row, false)} - onClickJobRow={toggleSidebarForJobRow} - onClickRow={(row) => selectRow(row, true)} - onShiftClickRow={shiftSelectRow} - onControlClickRow={(row) => selectRow(row, false)} - /> - - - - table.setPageIndex(page)} - onRowsPerPageChange={(e) => table.setPageSize(Number(e.target.value))} - colSpan={table.getVisibleLeafColumns().length} - showFirstButton={true} - showLastButton={true} - /> - - -
-
- - {debug &&
{JSON.stringify(table.getState(), null, 2)}
} + onRefresh={onRefresh} + onAddAnnotationColumn={addAnnotationCol} + onRemoveAnnotationColumn={removeAnnotationCol} + onEditAnnotationColumn={editAnnotationCol} + onGroupsChanged={onGroupingChange} + toggleColumnVisibility={onColumnVisibilityChange} + getJobsService={getJobsService} + updateJobsService={updateJobsService} + onClearFilters={clearFilters} + onAddCustomView={addCustomView} + onDeleteCustomView={deleteCustomView} + onLoadCustomView={loadCustomView} + /> + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + { + setTextFieldRef(header.id, ref) + }} + /> + ))} + + ))} + + + 0} + columns={table.getVisibleLeafColumns()} + topLevelRows={topLevelRows} + sidebarJobId={sidebarJobId} + onLoadMoreSubRows={onLoadMoreSubRows} + onClickRowCheckbox={(row) => selectRow(row, false)} + onClickJobRow={toggleSidebarForJobRow} + onClickRow={(row) => selectRow(row, true)} + onShiftClickRow={shiftSelectRow} + onControlClickRow={(row) => selectRow(row, false)} + /> +
+
+ table.setPageIndex(page)} + onRowsPerPageChange={(e) => table.setPageSize(Number(e.target.value))} + colSpan={table.getVisibleLeafColumns().length} + showFirstButton={true} + showLastButton={true} + /> + + {debug &&
{JSON.stringify(table.getState(), null, 2)}
} +
{sidebarJobDetails !== undefined && ( @@ -804,7 +804,9 @@ const JobsTableBody = ({ }: JobsTableBodyProps) => { const canDisplay = !dataIsLoading && topLevelRows.length > 0 return ( - + {!canDisplay && ( {dataIsLoading && topLevelRows.length === 0 && ( diff --git a/internal/lookout/ui/yarn.lock b/internal/lookout/ui/yarn.lock index 9c9d1e6fdd1..094e576f2fb 100644 --- a/internal/lookout/ui/yarn.lock +++ b/internal/lookout/ui/yarn.lock @@ -1321,13 +1321,6 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" - integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== - dependencies: - regenerator-runtime "^0.13.11" - "@babel/runtime@^7.22.10": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.11.tgz#7a9ba3bbe406ad6f9e8dd4da2ece453eb23a77a4" @@ -1528,6 +1521,23 @@ source-map "^0.5.7" stylis "4.1.3" +"@emotion/babel-plugin@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" + integrity sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/serialize" "^1.1.2" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + "@emotion/cache@^11.10.5": version "11.10.5" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12" @@ -1560,6 +1570,11 @@ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7" integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ== +"@emotion/hash@^0.9.1": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" + integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== + "@emotion/is-prop-valid@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83" @@ -1609,6 +1624,17 @@ "@emotion/utils" "^1.2.0" csstype "^3.0.2" +"@emotion/serialize@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.2.tgz#017a6e4c9b8a803bd576ff3d52a0ea6fa5a62b51" + integrity sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA== + dependencies: + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" + csstype "^3.0.2" + "@emotion/sheet@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c" @@ -1619,28 +1645,38 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== -"@emotion/styled@^11.10.5": - version "11.10.5" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.5.tgz#1fe7bf941b0909802cb826457e362444e7e96a79" - integrity sha512-8EP6dD7dMkdku2foLoruPCNkRevzdcBaY6q0l0OsbyJK+x8D9HWjX27ARiSIKNF634hY9Zdoedh8bJCiva8yZw== +"@emotion/styled@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.0.tgz#26b75e1b5a1b7a629d7c0a8b708fbf5a9cdce346" + integrity sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng== dependencies: "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.10.5" - "@emotion/is-prop-valid" "^1.2.0" - "@emotion/serialize" "^1.1.1" - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" - "@emotion/utils" "^1.2.0" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/is-prop-valid" "^1.2.1" + "@emotion/serialize" "^1.1.2" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" "@emotion/unitless@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db" integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw== +"@emotion/unitless@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" + integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== + "@emotion/use-insertion-effect-with-fallbacks@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df" integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A== +"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963" + integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== + "@emotion/utils@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561" @@ -1703,6 +1739,33 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@floating-ui/core@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.4.1.tgz#0d633f4b76052668afb932492ac452f7ebe97f17" + integrity sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ== + dependencies: + "@floating-ui/utils" "^0.1.1" + +"@floating-ui/dom@^1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.1.tgz#88b70defd002fe851f17b4a25efb2d3c04d7a8d7" + integrity sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw== + dependencies: + "@floating-ui/core" "^1.4.1" + "@floating-ui/utils" "^0.1.1" + +"@floating-ui/react-dom@^2.0.1": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.2.tgz#fab244d64db08e6bed7be4b5fcce65315ef44d20" + integrity sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ== + dependencies: + "@floating-ui/dom" "^1.5.1" + +"@floating-ui/utils@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.1.tgz#1a5b1959a528e374e8037c4396c3e825d6cf4a83" + integrity sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw== + "@humanwhocodes/config-array@^0.11.6": version "0.11.7" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" @@ -2143,24 +2206,25 @@ prop-types "^15.8.1" react-is "^18.2.0" -"@mui/base@5.0.0-beta.8": - version "5.0.0-beta.8" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.8.tgz#a0a9531ae9147be92d17e4f0e3b9accc57916841" - integrity sha512-b4vVjMZx5KzzEMf4arXKoeV5ZegAMOoPwoy1vfUBwhvXc2QtaaAyBp50U7OA2L06Leubc1A+lEp3eqwZoFn87g== +"@mui/base@5.0.0-beta.13": + version "5.0.0-beta.13" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.13.tgz#3bae94c39752546d84a67d4ca73486b7c4923a89" + integrity sha512-uC0l97pBspfDAp+iz2cJq8YZ8Sd9i73V77+WzUiOAckIVEyCm5dyVDZCCO2/phmzckVEeZCGcytybkjMQuhPQw== dependencies: - "@babel/runtime" "^7.22.6" + "@babel/runtime" "^7.22.10" "@emotion/is-prop-valid" "^1.2.1" + "@floating-ui/react-dom" "^2.0.1" "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.1" + "@mui/utils" "^5.14.7" "@popperjs/core" "^2.11.8" - clsx "^1.2.1" + clsx "^2.0.0" prop-types "^15.8.1" react-is "^18.2.0" -"@mui/core-downloads-tracker@^5.14.1": - version "5.14.1" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.1.tgz#af156cb3e15b202f5c09f66e7d8b71ca86aef525" - integrity sha512-mIa1WmDmNr1LoupV1Rbxt9bTFKMbIn10RHG1bnZ/FJCkAYpuU/D4n+R+ttiycgcZNngU++zyh/OQeJblzbQPzg== +"@mui/core-downloads-tracker@^5.14.7": + version "5.14.7" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.7.tgz#95bed2487bf59632125a13b8eb8f4c21e460afec" + integrity sha512-sCWTUNElBPgB30iLvWe3PU7SIlTKZNf6/E/sko85iHVeHCM6WPkDw+y89CrZYjhFNmPqt2fIQM/pZu+rP2lFLA== "@mui/icons-material@^5.14.3": version "5.14.6" @@ -2183,19 +2247,19 @@ prop-types "^15.8.1" react-is "^18.2.0" -"@mui/material@^5.13.6": - version "5.14.1" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.1.tgz#2711e4ca5c9bdc67b916d01faee650a7a5260bb8" - integrity sha512-WtsgYuageTunLfxH3Ri+o1RuQTFImtRHxMcVNyD0Hhd2/znjW6KODNz0XfjvLRnNCAynBxZNiflcoIBW40h9PQ== +"@mui/material@^5.14.4": + version "5.14.7" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.7.tgz#6c2c0de8a625562f789e1bb33cb4cfc8cf20bdb0" + integrity sha512-jIZj9F7zMv6IlyaYDVv5M2Kp20jIX8c0kzuwteySHS/A0IvPVyomQEPtWc51MCbpDNCqzwoZUp3rQtA2lI8k7A== dependencies: - "@babel/runtime" "^7.22.6" - "@mui/base" "5.0.0-beta.8" - "@mui/core-downloads-tracker" "^5.14.1" - "@mui/system" "^5.14.1" + "@babel/runtime" "^7.22.10" + "@mui/base" "5.0.0-beta.13" + "@mui/core-downloads-tracker" "^5.14.7" + "@mui/system" "^5.14.7" "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.1" + "@mui/utils" "^5.14.7" "@types/react-transition-group" "^4.4.6" - clsx "^1.2.1" + clsx "^2.0.0" csstype "^3.1.2" prop-types "^15.8.1" react-is "^18.2.0" @@ -2210,13 +2274,13 @@ "@mui/utils" "^5.10.16" prop-types "^15.8.1" -"@mui/private-theming@^5.13.7": - version "5.13.7" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.13.7.tgz#2f8ef5da066f3c6c6423bd4260d003a28d10b099" - integrity sha512-qbSr+udcij5F9dKhGX7fEdx2drXchq7htLNr2Qg2Ma+WJ6q0ERlEqGSBiPiVDJkptcjeVL4DGmcf1wl5+vD4EA== +"@mui/private-theming@^5.14.7": + version "5.14.7" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.14.7.tgz#c9fec31e59bf66b12959e724b0e8ec3bb4a3d923" + integrity sha512-Y86+hmDnJab2Ka42PgxKpK3oL7EiacbeeX3X/lG9LGO0wSc45wZjHeTfIlVSkkUCkexiMKEJp5NlSjZhr27NRQ== dependencies: - "@babel/runtime" "^7.22.5" - "@mui/utils" "^5.13.7" + "@babel/runtime" "^7.22.10" + "@mui/utils" "^5.14.7" prop-types "^15.8.1" "@mui/styled-engine@^5.10.16": @@ -2229,12 +2293,12 @@ csstype "^3.1.1" prop-types "^15.8.1" -"@mui/styled-engine@^5.13.2": - version "5.13.2" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.13.2.tgz#c87bd61c0ab8086d34828b6defe97c02bcd642ef" - integrity sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw== +"@mui/styled-engine@^5.14.7": + version "5.14.7" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.14.7.tgz#aaacec6c87bcc9a180b2da062c613213af10f2e3" + integrity sha512-hKBETEDsIAkL8/mBwPiQj/vw28OeIhMXC3Tvj4J2bb9snxAKpiZioR1PwqP+6P41twsC/GKBd0Vr9oaWYaHuMg== dependencies: - "@babel/runtime" "^7.21.0" + "@babel/runtime" "^7.22.10" "@emotion/cache" "^11.11.0" csstype "^3.1.2" prop-types "^15.8.1" @@ -2253,17 +2317,17 @@ csstype "^3.1.1" prop-types "^15.8.1" -"@mui/system@^5.14.1": - version "5.14.1" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.1.tgz#ec8ae69f63963b5916dad4bca2f8a86a001a2392" - integrity sha512-u+xlsU34Jdkgx1CxmBnIC4Y08uPdVX5iEd3S/1dggDFtOGp+Lj8xmKRJAQ8PJOOJLOh8pDwaZx4AwXikL4l1QA== +"@mui/system@^5.14.7": + version "5.14.7" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.7.tgz#b08e23f9151d38186ab12dd618906abd4d73d203" + integrity sha512-jeZtHglc+Pi6qjGoopT6O4RqYXVBMqHVOsjMGP0hxGSSPm1T4gsAu7jU8eqGx9YwwjvvJ0eotTjFqw7iJ6qE2Q== dependencies: - "@babel/runtime" "^7.22.6" - "@mui/private-theming" "^5.13.7" - "@mui/styled-engine" "^5.13.2" + "@babel/runtime" "^7.22.10" + "@mui/private-theming" "^5.14.7" + "@mui/styled-engine" "^5.14.7" "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.1" - clsx "^1.2.1" + "@mui/utils" "^5.14.7" + clsx "^2.0.0" csstype "^3.1.2" prop-types "^15.8.1" @@ -2288,12 +2352,12 @@ prop-types "^15.8.1" react-is "^18.2.0" -"@mui/utils@^5.13.7", "@mui/utils@^5.14.1": - version "5.14.1" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.1.tgz#29696371016552a6eb3af975bc7af429ec88b29a" - integrity sha512-39KHKK2JeqRmuUcLDLwM+c2XfVC136C5/yUyQXmO2PVbOb2Bol4KxtkssEqCbTwg87PSCG3f1Tb0keRsK7cVGw== +"@mui/utils@^5.14.7": + version "5.14.7" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.7.tgz#3677bcabe032f1185e151f57d8c1a166df3ae0a1" + integrity sha512-RtheP/aBoPogVdi8vj8Vo2IFnRa4mZVmnD0RGlVZ49yF60rZs+xP4/KbpIrTr83xVs34QmHQ2aQ+IX7I0a0dDw== dependencies: - "@babel/runtime" "^7.22.6" + "@babel/runtime" "^7.22.10" "@types/prop-types" "^15.7.5" "@types/react-is" "^18.2.1" prop-types "^15.8.1" @@ -2352,10 +2416,10 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== -"@remix-run/router@1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.1.tgz#fea7ac35ae4014637c130011f59428f618730498" - integrity sha512-bgVQM4ZJ2u2CM8k1ey70o1ePFXsEzYVZoWghh6WjM8p59jQ7HxzbHW4SbnWFG7V9ig9chLawQxDTZ3xzOF8MkQ== +"@remix-run/router@1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.2.tgz#cba1cf0a04bc04cb66027c51fa600e9cbc388bc8" + integrity sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A== "@rollup/plugin-babel@^5.2.0": version "5.3.1" @@ -2914,10 +2978,10 @@ dependencies: "@types/react" "*" -"@types/react-dom@^16.9.9": - version "16.9.16" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.16.tgz#c591f2ed1c6f32e9759dfa6eb4abfd8041f29e39" - integrity sha512-Oqc0RY4fggGA3ltEgyPLc3IV9T73IGoWjkONbsyJ3ZBn+UPPCYpU2ec0i3cEbJuEdZtkqcCF2l1zf2pBdgUGSg== +"@types/react-dom@^16.9.19": + version "16.9.19" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.19.tgz#6a139c26b02dec533a7fa131f084561babb10a8f" + integrity sha512-xC8D280Bf6p0zguJ8g62jcEOKZiUbx9sIe6O3tT/lKfR87A7A6g65q13z6D5QUMIa/6yFPkNhqjF5z/VVZEYqQ== dependencies: "@types/react" "^16" @@ -4130,6 +4194,13 @@ async@^3.2.3: resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== +asynciterator.prototype@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62" + integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg== + dependencies: + has-symbols "^1.0.3" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -4604,6 +4675,11 @@ clsx@^1.0.4, clsx@^1.1.0, clsx@^1.2.1: resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== +clsx@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b" + integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -5574,7 +5650,7 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19 string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" -es-abstract@^1.21.2: +es-abstract@^1.21.2, es-abstract@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== @@ -5624,6 +5700,26 @@ es-array-method-boxes-properly@^1.0.0: resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-iterator-helpers@^1.0.12: + version "1.0.14" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.14.tgz#19cd7903697d97e21198f3293b55e8985791c365" + integrity sha512-JgtVnwiuoRuzLvqelrvN3Xu7H9bu2ap/kQ2CrM62iidP8SKuD99rWU3CJy++s7IVL2qb/AjXPGR/E7i9ngd/Cw== + dependencies: + asynciterator.prototype "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-set-tostringtag "^2.0.1" + function-bind "^1.1.1" + get-intrinsic "^1.2.1" + globalthis "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + iterator.prototype "^1.1.0" + safe-array-concat "^1.0.0" + es-module-lexer@^0.9.0: version "0.9.3" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" @@ -5835,7 +5931,7 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== -eslint-plugin-react@^7.27.1, eslint-plugin-react@^7.31.11: +eslint-plugin-react@^7.27.1: version "7.31.11" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz#011521d2b16dcf95795df688a4770b4eaab364c8" integrity sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw== @@ -5856,6 +5952,28 @@ eslint-plugin-react@^7.27.1, eslint-plugin-react@^7.31.11: semver "^6.3.0" string.prototype.matchall "^4.0.8" +eslint-plugin-react@^7.33.1: + version "7.33.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608" + integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw== + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + es-iterator-helpers "^1.0.12" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.1" + string.prototype.matchall "^4.0.8" + eslint-plugin-testing-library@^5.0.1: version "5.9.1" resolved "https://registry.yarnpkg.com/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.9.1.tgz#12e4bd34c48683ee98af4df2e3318ec9f51dcf8a" @@ -6948,6 +7066,13 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -6989,7 +7114,7 @@ is-core-module@^2.8.1, is-core-module@^2.9.0: dependencies: has "^1.0.3" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -7006,6 +7131,13 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -7016,6 +7148,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -7028,6 +7167,11 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3: resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" integrity sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g== +is-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -7088,6 +7232,11 @@ is-root@^2.1.0: resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== +is-set@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -7126,6 +7275,11 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -7133,6 +7287,14 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -7197,6 +7359,17 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterator.prototype@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.0.tgz#690c88b043d821f783843aaf725d7ac3b62e3b46" + integrity sha512-rjuhAk1AJ1fssphHD0IFV6TWL40CwRZ53FrztKx43yk2v6rguBYsY4Bj1VU4HmoMmKwZUlx7mfnhDf9cOp4YTw== + dependencies: + define-properties "^1.1.4" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + has-tostringtag "^1.0.0" + reflect.getprototypeof "^1.0.3" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -9564,20 +9737,20 @@ react-refresh@^0.11.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== -react-router-dom@6.14.1: - version "6.14.1" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.1.tgz#0ad7ba7abdf75baa61169d49f096f0494907a36f" - integrity sha512-ssF6M5UkQjHK70fgukCJyjlda0Dgono2QGwqGvuk7D+EDGHdacEN3Yke2LTMjkrpHuFwBfDFsEjGVXBDmL+bWw== +react-router-dom@6.14.2: + version "6.14.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.2.tgz#88f520118b91aa60233bd08dbd3fdcaea3a68488" + integrity sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg== dependencies: - "@remix-run/router" "1.7.1" - react-router "6.14.1" + "@remix-run/router" "1.7.2" + react-router "6.14.2" -react-router@6.14.1: - version "6.14.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.1.tgz#5e82bcdabf21add859dc04b1859f91066b3a5810" - integrity sha512-U4PfgvG55LdvbQjg5Y9QRWyVxIdO1LlpYT7x+tMAxd9/vmiPuJhIwdxZuIQLN/9e3O4KFDHYfR9gzGeYMasW8g== +react-router@6.14.2: + version "6.14.2" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.2.tgz#1f60994d8c369de7b8ba7a78d8f7ec23df76b300" + integrity sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ== dependencies: - "@remix-run/router" "1.7.1" + "@remix-run/router" "1.7.2" react-scripts@^5.0.1: version "5.0.1" @@ -9743,6 +9916,18 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^1.0.0" +reflect.getprototypeof@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.3.tgz#2738fd896fcc3477ffbd4190b40c2458026b6928" + integrity sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.1" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + regenerate-unicode-properties@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" @@ -9911,7 +10096,7 @@ resolve@^1.22.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.3: +resolve@^2.0.0-next.3, resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== @@ -11272,7 +11457,35 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-typed-array@^1.1.10, which-typed-array@^1.1.11: +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + +which-typed-array@^1.1.10, which-typed-array@^1.1.11, which-typed-array@^1.1.9: version "1.1.11" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== diff --git a/internal/testsuite/app.go b/internal/testsuite/app.go index 218c7269288..05a0c4402c2 100644 --- a/internal/testsuite/app.go +++ b/internal/testsuite/app.go @@ -17,6 +17,8 @@ import ( "github.com/hashicorp/go-multierror" "github.com/mattn/go-zglob" "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/push" "github.com/renstrom/shortuuid" "golang.org/x/sync/errgroup" apimachineryYaml "k8s.io/apimachinery/pkg/util/yaml" @@ -29,6 +31,8 @@ import ( "github.com/armadaproject/armada/pkg/client" ) +const metricsPrefix = "armada_testsuite_" + type App struct { // Parameters passed to the CLI by the user. Params *Params @@ -45,7 +49,13 @@ type App struct { // and that they can be provided either dynamically on a command line, or // statically in a config file that's reused between command runs. type Params struct { + // Armada connection details. ApiConnectionDetails *client.ApiConnectionDetails + // If non-empty, push metrics containing test results to a Prometheus push gateway with this url. + PrometheusPushGatewayUrl string + // Exported metrics are annotated with job=PrometheusPushGatewayJobName. + // Must be non-empty. + PrometheusPushGatewayJobName string } // New instantiates an App with default parameters, including standard output @@ -107,7 +117,7 @@ func TestSpecFromFilePath(filePath string) (*api.TestSpec, error) { return nil, err } - // Randomise jobSetName for each test to ensure we're only getting events for this run. + // Randomise job set for each test to ensure we're only getting events for this run. fileName := filepath.Base(filePath) fileName = strings.TrimSuffix(fileName, filepath.Ext(fileName)) testSpec.JobSetId = fileName + "-" + shortuuid.New() @@ -126,6 +136,18 @@ type TestSuiteReport struct { TestCaseReports []*TestCaseReport } +func (tsr *TestSuiteReport) Describe(c chan<- *prometheus.Desc) { + for _, tcr := range tsr.TestCaseReports { + tcr.Describe(c) + } +} + +func (tsr *TestSuiteReport) Collect(c chan<- prometheus.Metric) { + for _, tcr := range tsr.TestCaseReports { + tcr.Collect(c) + } +} + type TestCaseReport struct { Out *bytes.Buffer Start time.Time @@ -133,6 +155,100 @@ type TestCaseReport struct { FailureReason string BenchmarkReport *eventbenchmark.TestCaseBenchmarkReport TestSpec *api.TestSpec + + // Prometheus metric descriptions. + // Test start time in seconds since the epoch. + startTimePrometheusDesc *prometheus.Desc + // Test finish time in seconds since the epoch. + finishTimePrometheusDesc *prometheus.Desc + // Outputs 1 on test timeout. + testTimeoutPrometheusDesc *prometheus.Desc + // Outputs 1 on test failure, not including timeouts. + testFailurePrometheusDesc *prometheus.Desc +} + +func NewTestCaseReport(testSpec *api.TestSpec) *TestCaseReport { + rv := &TestCaseReport{ + Start: time.Now(), + TestSpec: testSpec, + } + rv.initialiseMetrics() + return rv +} + +func (r *TestCaseReport) initialiseMetrics() { + r.startTimePrometheusDesc = prometheus.NewDesc( + metricsPrefix+"test_start_time", + "The time at which a test started.", + []string{"testcase"}, + nil, + ) + r.finishTimePrometheusDesc = prometheus.NewDesc( + metricsPrefix+"test_finish_time", + "The time at which a test finished.", + []string{"testcase"}, + nil, + ) + r.testTimeoutPrometheusDesc = prometheus.NewDesc( + metricsPrefix+"test_timeout", + "Outputs 1 on test timeout and 0 otherwise.", + []string{"testcase"}, + nil, + ) + r.testFailurePrometheusDesc = prometheus.NewDesc( + metricsPrefix+"test_failure", + "Outputs 1 on test failure, not including timeout, and 0 otherwise.", + []string{"testcase"}, + nil, + ) +} + +func (r *TestCaseReport) Describe(c chan<- *prometheus.Desc) { + c <- r.startTimePrometheusDesc + c <- r.finishTimePrometheusDesc + c <- r.testTimeoutPrometheusDesc + c <- r.testFailurePrometheusDesc +} + +func (r *TestCaseReport) Collect(c chan<- prometheus.Metric) { + c <- prometheus.MustNewConstMetric( + r.startTimePrometheusDesc, + prometheus.CounterValue, + float64(r.Start.Unix()), + r.TestSpec.Name, + ) + c <- prometheus.MustNewConstMetric( + r.finishTimePrometheusDesc, + prometheus.CounterValue, + float64(r.Finish.Unix()), + r.TestSpec.Name, + ) + + // Test failures always contain either "unexpected event for job" or "error asserting failure reason". + // TODO(albin): Improve this. + testFailure := 0.0 + if strings.Contains(r.FailureReason, "unexpected event for job") || strings.Contains(r.FailureReason, "error asserting failure reason") { + testFailure = 1.0 + } + c <- prometheus.MustNewConstMetric( + r.testFailurePrometheusDesc, + prometheus.GaugeValue, + testFailure, + r.TestSpec.Name, + ) + + // We assume that any other failures are due to timeout. + // TODO(albin): Improve this. + testTimeout := 0.0 + if r.FailureReason != "" && testFailure == 0 { + testTimeout = 1.0 + } + c <- prometheus.MustNewConstMetric( + r.testTimeoutPrometheusDesc, + prometheus.GaugeValue, + testTimeout, + r.TestSpec.Name, + ) } func (report *TestSuiteReport) NumSuccesses() int { @@ -203,9 +319,26 @@ func (a *App) RunTests(ctx context.Context, testSpecs []*api.TestSpec) (*TestSui return nil, err } + // Optionally push metrics. + if a.Params.PrometheusPushGatewayUrl != "" { + if err := pushTestSuiteReportMetrics(rv, a.Params.PrometheusPushGatewayUrl, a.Params.PrometheusPushGatewayJobName); err != nil { + return nil, err + } + } return rv, nil } +func pushTestSuiteReportMetrics(tsr *TestSuiteReport, url, job string) error { + pusher := push.New(url, job) + pusher.Collector(tsr) + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + if err := pusher.PushContext(ctx); err != nil { + return errors.WithStack(err) + } + return nil +} + // UnmarshalTestCase unmarshalls bytes into a TestSpec. func UnmarshalTestCase(yamlBytes []byte, testSpec *api.TestSpec) error { var result *multierror.Error diff --git a/internal/testsuite/testrunner.go b/internal/testsuite/testrunner.go index 2feed1285c0..3108cc9888d 100644 --- a/internal/testsuite/testrunner.go +++ b/internal/testsuite/testrunner.go @@ -49,11 +49,8 @@ func (report *TestCaseReport) JunitTestCase() junit.Testcase { } func (srv *TestRunner) Run(ctx context.Context) (err error) { - report := &TestCaseReport{ - Out: &bytes.Buffer{}, - Start: time.Now(), - TestSpec: srv.testSpec, - } + report := NewTestCaseReport(srv.testSpec) + report.Out = &bytes.Buffer{} out := io.MultiWriter(srv.Out, report.Out) fmt.Fprintf(out, "test case started %s\n", srv.testSpec.ShortString())