From 1cd1cd72bce74646939e0579221c51f2240737dd Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:04:37 -0400 Subject: [PATCH 01/10] debug daily notification --- .github/workflows/daily_tests.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/daily_tests.yml b/.github/workflows/daily_tests.yml index 1dd06eab0..ca314bb5d 100644 --- a/.github/workflows/daily_tests.yml +++ b/.github/workflows/daily_tests.yml @@ -42,14 +42,21 @@ jobs: if: | ${{ always() && ( - needs.DevTests.result == 'failure' - || needs.LiveServices.result == 'failure' - || needs.BuildTests.result == 'failure' - || needs.ExampleDataCache.result == 'failure' - || needs.ExampleDataTests.result == 'failure' + needs.DevTests.result == 'failure' + || needs.LiveServices.result == 'failure' + || needs.BuildTests.result == 'failure' + || needs.ExampleDataCache.result == 'failure' + || needs.ExampleDataTests.result == 'failure' ) }} steps: + - name: Printout which failed + run: | + echo "DevTests: ${{ needs.DevTests.result }}" + echo "LiveServices: ${{ needs.LiveServices.result }}" + echo "BuildTests: ${{ needs.BuildTests.result }}" + echo "ExampleDataCache: ${{ needs.ExampleDataCache.result }}" + echo "ExampleDataTests: ${{ needs.ExampleDataTests.result }}" - uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com From 863fb9042f70f814fc47ff884612d20e0e10243a Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:53:12 -0400 Subject: [PATCH 02/10] debugging notification --- .github/workflows/daily_tests.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/daily_tests.yml b/.github/workflows/daily_tests.yml index ca314bb5d..4689da1ff 100644 --- a/.github/workflows/daily_tests.yml +++ b/.github/workflows/daily_tests.yml @@ -57,6 +57,15 @@ jobs: echo "BuildTests: ${{ needs.BuildTests.result }}" echo "ExampleDataCache: ${{ needs.ExampleDataCache.result }}" echo "ExampleDataTests: ${{ needs.ExampleDataTests.result }}" + - name: Printout logic trigger + run: | + ( + needs.DevTests.result == 'failure' + || needs.LiveServices.result == 'failure' + || needs.BuildTests.result == 'failure' + || needs.ExampleDataCache.result == 'failure' + || needs.ExampleDataTests.result == 'failure' + ) - uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com From e0257bf3bca412125a960db68d8b1ee31572805f Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:29:40 -0400 Subject: [PATCH 03/10] try flattening --- .github/workflows/daily_tests.yml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/daily_tests.yml b/.github/workflows/daily_tests.yml index 4689da1ff..26418cedc 100644 --- a/.github/workflows/daily_tests.yml +++ b/.github/workflows/daily_tests.yml @@ -41,13 +41,7 @@ jobs: needs: [DevTests, LiveServices, BuildTests, ExampleDataCache, ExampleDataTests] if: | ${{ - always() && ( - needs.DevTests.result == 'failure' - || needs.LiveServices.result == 'failure' - || needs.BuildTests.result == 'failure' - || needs.ExampleDataCache.result == 'failure' - || needs.ExampleDataTests.result == 'failure' - ) + always() && (needs.DevTests.result == 'failure' || needs.LiveServices.result == 'failure' || needs.BuildTests.result == 'failure' || needs.ExampleDataCache.result == 'failure' || needs.ExampleDataTests.result == 'failure') }} steps: - name: Printout which failed @@ -58,14 +52,7 @@ jobs: echo "ExampleDataCache: ${{ needs.ExampleDataCache.result }}" echo "ExampleDataTests: ${{ needs.ExampleDataTests.result }}" - name: Printout logic trigger - run: | - ( - needs.DevTests.result == 'failure' - || needs.LiveServices.result == 'failure' - || needs.BuildTests.result == 'failure' - || needs.ExampleDataCache.result == 'failure' - || needs.ExampleDataTests.result == 'failure' - ) + run: ${{ needs.DevTests.result }} == 'failure' || ${{ needs.LiveServices.result }} == 'failure' || ${{ needs.BuildTests.result }} == 'failure' || ${{ needs.ExampleDataCache.result }} == 'failure' || ${{ needs.ExampleDataTests.result }} == 'failure' - uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com From 023ccd04e9309642b2e747f9608f6eaec4dabd55 Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:57:02 -0400 Subject: [PATCH 04/10] Update daily_tests.yml --- .github/workflows/daily_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily_tests.yml b/.github/workflows/daily_tests.yml index 26418cedc..32b8b1edd 100644 --- a/.github/workflows/daily_tests.yml +++ b/.github/workflows/daily_tests.yml @@ -51,8 +51,8 @@ jobs: echo "BuildTests: ${{ needs.BuildTests.result }}" echo "ExampleDataCache: ${{ needs.ExampleDataCache.result }}" echo "ExampleDataTests: ${{ needs.ExampleDataTests.result }}" - - name: Printout logic trigger - run: ${{ needs.DevTests.result }} == 'failure' || ${{ needs.LiveServices.result }} == 'failure' || ${{ needs.BuildTests.result }} == 'failure' || ${{ needs.ExampleDataCache.result }} == 'failure' || ${{ needs.ExampleDataTests.result }} == 'failure' + #- name: Printout logic trigger + # run: ${{ needs.DevTests.result }} == 'failure' || ${{ needs.LiveServices.result }} == 'failure' || ${{ needs.BuildTests.result }} == 'failure' || ${{ needs.ExampleDataCache.result }} == 'failure' || ${{ needs.ExampleDataTests.result }} == 'failure' - uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com From 0930ee88c295a9c04359bf878ed0c2aebdbd3021 Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Fri, 14 Jun 2024 11:17:33 -0700 Subject: [PATCH 05/10] Add form and instance manager tests. Organize dependencies file differently to support proper DOM access --- .../core/components/InstanceListItem.ts | 16 +- .../core/components/InstanceManager.js | 39 +- .../core/components/JSONSchemaInput.js | 9 +- .../frontend/core/components/pages/Page.js | 2 +- .../components/pages/contact-us/Contact.js | 2 +- .../pages/documentation/Documentation.js | 2 +- .../pages/guided-mode/GuidedHome.js | 2 +- .../components/pages/settings/SettingsPage.js | 2 +- .../frontend/core/components/table/Cell.ts | 3 +- src/electron/frontend/core/errors.ts | 2 +- src/electron/frontend/core/index.ts | 2 +- src/electron/frontend/core/lotties.ts | 19 + .../{dependencies.js => notifications.ts} | 20 - src/electron/frontend/core/server/index.ts | 2 +- src/electron/frontend/utils/random.ts | 10 +- tests/components/InstanceManager.test.ts | 120 ++++++ tests/components/forms.test.ts | 376 ++++++++++++++++++ tests/metadata.test.ts | 181 +-------- tests/utils.test.ts | 6 +- vite.config.js | 29 +- 20 files changed, 570 insertions(+), 274 deletions(-) create mode 100644 src/electron/frontend/core/lotties.ts rename src/electron/frontend/core/{dependencies.js => notifications.ts} (82%) create mode 100644 tests/components/InstanceManager.test.ts create mode 100644 tests/components/forms.test.ts diff --git a/src/electron/frontend/core/components/InstanceListItem.ts b/src/electron/frontend/core/components/InstanceListItem.ts index 61ebc2afe..f2c8b5896 100644 --- a/src/electron/frontend/core/components/InstanceListItem.ts +++ b/src/electron/frontend/core/components/InstanceListItem.ts @@ -7,7 +7,6 @@ export class InstanceListItem extends LitElement { declare label: string declare status: string declare selected: boolean - declare onRemoved?: Function static get styles() { return css` @@ -120,7 +119,7 @@ export class InstanceListItem extends LitElement { } } - constructor({ label, status, selected, onRemoved, id, ...metadata } = { + constructor({ label, status, selected, id, ...metadata } = { label: "", status: "", selected: false @@ -131,7 +130,6 @@ export class InstanceListItem extends LitElement { this.status = status; this.selected = selected; this.metadata = metadata - if (this.onRemoved) this.onRemoved = onRemoved; } #onClick () { @@ -154,18 +152,6 @@ export class InstanceListItem extends LitElement { >${this.label}
- ${this.onRemoved - ? html`x` - : ""} ` } diff --git a/src/electron/frontend/core/components/InstanceManager.js b/src/electron/frontend/core/components/InstanceManager.js index ad0d8a1e6..cfb4e1f86 100644 --- a/src/electron/frontend/core/components/InstanceManager.js +++ b/src/electron/frontend/core/components/InstanceManager.js @@ -1,6 +1,6 @@ import { LitElement, css, html } from "lit"; import "./Button"; -import { notify } from "../dependencies"; +import { notify } from "../notifications"; import { Accordion } from "./Accordion"; import { InstanceListItem } from "./InstanceListItem"; import { checkStatus } from "../validation"; @@ -142,7 +142,6 @@ export class InstanceManager extends LitElement { this.header = props.header; this.instanceType = props.instanceType ?? "Instance"; if (props.onAdded) this.onAdded = props.onAdded; - if (props.onRemoved) this.onRemoved = props.onRemoved; if (props.onDisplay) this.onDisplay = props.onDisplay; this.controls = props.controls ?? []; } @@ -157,8 +156,12 @@ export class InstanceManager extends LitElement { } else return content; }; + getInstance(id) { + return this.#items.find((item) => item.id === id); + } + updateState = (id, state) => { - const item = this.#items.find((i) => i.id === id); + const item = this.getInstance(id); item.status = state; @@ -178,7 +181,6 @@ export class InstanceManager extends LitElement { }; // onAdded = () => {} - // onRemoved = () => {} toggleInput = (force) => { const newInfoDiv = this.shadowRoot.querySelector("#new-info"); @@ -248,34 +250,6 @@ export class InstanceManager extends LitElement { #items = []; #info = {}; - #onRemoved(ev) { - const parent = ev.target.parentNode; - const name = parent.getAttribute("data-instance"); - const ogPath = name.split("/"); - const path = [...ogPath]; - let target = toRender; - const key = path.pop(); - target = path.reduce((acc, cur) => acc[cur], target); - this.onRemoved(target[key], ogPath); - delete target[key]; - - if (parent.hasAttribute("selected")) { - const previous = parent.previousElementSibling?.getAttribute("data-instance"); - if (previous) this.#selected = previous; - else { - const next = parent.nextElementSibling?.getAttribute("data-instance"); - if (next) this.#selected = next; - else this.#selected = undefined; - } - } - - // parent.remove() - // const instance = this.shadowRoot.querySelector(`div[data-instance="${name}"]`) - // instance.remove() - - this.requestUpdate(); - } - #hideAll(chosenInstanceElement) { Array.from(this.shadowRoot.querySelectorAll("div[data-instance]")).forEach((instanceElement) => { if (instanceElement !== chosenInstanceElement) instanceElement.hidden = true; @@ -332,7 +306,6 @@ export class InstanceManager extends LitElement { id: key, label: key.split("/").pop(), selected: key === this.#selected, - onRemoved: this.#onRemoved.bind(this), ...info, }; diff --git a/src/electron/frontend/core/components/JSONSchemaInput.js b/src/electron/frontend/core/components/JSONSchemaInput.js index f9966222f..84f72db08 100644 --- a/src/electron/frontend/core/components/JSONSchemaInput.js +++ b/src/electron/frontend/core/components/JSONSchemaInput.js @@ -19,6 +19,7 @@ import { OptionalSection } from "./OptionalSection"; import { InspectorListItem } from "./InspectorList.js"; import { renderDateTime, resolveDateTime } from "./DateTimeSelector"; import { isObject } from "../../utils/typecheck"; +import { resolve } from "../../utils/promises"; const isDevelopment = !!import.meta.env; @@ -583,7 +584,7 @@ export class JSONSchemaInput extends LitElement { // onUpdate = () => {} // onValidate = () => {} - updateData(value, forceValidate = false) { + async updateData(value, forceValidate = false) { if (!forceValidate) { // Update the actual input element const inputElement = this.getElement(); @@ -603,9 +604,9 @@ export class JSONSchemaInput extends LitElement { const name = path.splice(-1)[0]; this.#updateData(fullPath, value); - this.#triggerValidation(name, path); // NOTE: Is asynchronous + const possiblePromise = this.#triggerValidation(name, path); - return true; + return resolve(possiblePromise, () => true) } getElement = () => this.shadowRoot.querySelector(".schema-input"); @@ -641,7 +642,7 @@ export class JSONSchemaInput extends LitElement { if (hooks.willTimeout !== false) this.#activateTimeoutValidation(name, path, hooks); }; - #triggerValidation = async (name, path) => { + #triggerValidation = (name, path) => { this.#clearTimeoutValidation(); return this.onValidate ? this.onValidate() diff --git a/src/electron/frontend/core/components/pages/Page.js b/src/electron/frontend/core/components/pages/Page.js index 886987dfb..8cda7e1e3 100644 --- a/src/electron/frontend/core/components/pages/Page.js +++ b/src/electron/frontend/core/components/pages/Page.js @@ -2,7 +2,7 @@ import { LitElement, html } from "lit"; import { run } from "../../../utils/run"; import { get, save } from "../../progress/index.js"; -import { dismissNotification, notify } from "../../dependencies.js"; +import { dismissNotification, notify } from "../../notifications"; import { isStorybook } from "../../globals.js"; import { mapSessions, merge } from "../../../utils/data"; diff --git a/src/electron/frontend/core/components/pages/contact-us/Contact.js b/src/electron/frontend/core/components/pages/contact-us/Contact.js index 2b8995dae..c2e35bbb5 100644 --- a/src/electron/frontend/core/components/pages/contact-us/Contact.js +++ b/src/electron/frontend/core/components/pages/contact-us/Contact.js @@ -2,7 +2,7 @@ import { html } from "lit"; import { contact_lottie } from "../../../../assets/lotties/contact-us-lotties.js"; import { Page } from "../Page.js"; -import { startLottie } from "../../../dependencies.js"; +import { startLottie } from "../../../lotties"; export class ContactPage extends Page { header = { diff --git a/src/electron/frontend/core/components/pages/documentation/Documentation.js b/src/electron/frontend/core/components/pages/documentation/Documentation.js index 77ca419d4..8a3b62dbe 100644 --- a/src/electron/frontend/core/components/pages/documentation/Documentation.js +++ b/src/electron/frontend/core/components/pages/documentation/Documentation.js @@ -2,7 +2,7 @@ import { html } from "lit"; import { docu_lottie } from "../../../../assets/lotties/documentation-lotties.js"; import { Page } from "../Page.js"; -import { startLottie } from "../../../dependencies.js"; +import { startLottie } from "../../../lotties"; import { Button } from "../../Button.js"; diff --git a/src/electron/frontend/core/components/pages/guided-mode/GuidedHome.js b/src/electron/frontend/core/components/pages/guided-mode/GuidedHome.js index a71dc03d6..8352f49a9 100644 --- a/src/electron/frontend/core/components/pages/guided-mode/GuidedHome.js +++ b/src/electron/frontend/core/components/pages/guided-mode/GuidedHome.js @@ -2,7 +2,7 @@ import { html } from "lit"; import { Page } from "../Page.js"; import { ProgressCard } from "./ProgressCard.js"; -import { startLottie } from "../../../dependencies.js"; +import { startLottie } from "../../../lotties"; import * as progress from "../../../progress/index.js"; import { newDataset } from "../../../../assets/lotties/index.js"; diff --git a/src/electron/frontend/core/components/pages/settings/SettingsPage.js b/src/electron/frontend/core/components/pages/settings/SettingsPage.js index ae866c70f..77746f42c 100644 --- a/src/electron/frontend/core/components/pages/settings/SettingsPage.js +++ b/src/electron/frontend/core/components/pages/settings/SettingsPage.js @@ -12,7 +12,7 @@ import { Button } from "../../Button.js"; import { global, remove, save } from "../../../progress/index.js"; import { merge, setUndefinedIfNotDeclared } from "../../../../utils/data"; -import { notyf } from "../../../dependencies.js"; +import { notyf } from "../../../notifications"; import { homeDirectory, testDataFolderPath } from "../../../globals.js"; import { SERVER_FILE_PATH, electron, path, port, fs } from "../../../../utils/electron"; diff --git a/src/electron/frontend/core/components/table/Cell.ts b/src/electron/frontend/core/components/table/Cell.ts index af458033b..97854d716 100644 --- a/src/electron/frontend/core/components/table/Cell.ts +++ b/src/electron/frontend/core/components/table/Cell.ts @@ -179,9 +179,8 @@ export class TableCell extends LitElement { if (!this.editable) return // Don't set value if not editable - if (this.input) this.input.set(value) // Ensure all operations are undoable + if (this.input) return this.input.set(value) // Ensure all operations are undoable else this.#value = value // Silently set value if not rendered yet - } #value diff --git a/src/electron/frontend/core/errors.ts b/src/electron/frontend/core/errors.ts index 8c64621d1..d60c6a5a4 100644 --- a/src/electron/frontend/core/errors.ts +++ b/src/electron/frontend/core/errors.ts @@ -1,2 +1,2 @@ -import { notify } from './dependencies' +import { notify } from './notifications' export const onThrow = (message: string, id?: string) => notify(id ? `[${id}]: ${message}` : message, "error", 7000); diff --git a/src/electron/frontend/core/index.ts b/src/electron/frontend/core/index.ts index 815094fc8..ef44860c3 100644 --- a/src/electron/frontend/core/index.ts +++ b/src/electron/frontend/core/index.ts @@ -9,7 +9,7 @@ import { Dashboard } from './components/Dashboard.js' import { notyf, notify -} from './dependencies.js' +} from './notifications' import Swal from 'sweetalert2' import { loadServerEvents, pythonServerOpened } from "./server/index.js"; diff --git a/src/electron/frontend/core/lotties.ts b/src/electron/frontend/core/lotties.ts new file mode 100644 index 000000000..b5969254f --- /dev/null +++ b/src/electron/frontend/core/lotties.ts @@ -0,0 +1,19 @@ +import lottie from "lottie-web"; + +import checkChromatic from "chromatic/isChromatic"; +export const isChromatic = checkChromatic(); + +export const startLottie = (lottieElement: HTMLElement, animationData: any) => { + lottieElement.innerHTML = ""; + const thisLottie = lottie.loadAnimation({ + container: lottieElement, + animationData, + renderer: "svg", + loop: !isChromatic, + autoplay: !isChromatic, + }); + + if (isChromatic) thisLottie.goToAndStop(thisLottie.getDuration(true) - 1, true); // Go to last frame + + return thisLottie; +}; \ No newline at end of file diff --git a/src/electron/frontend/core/dependencies.js b/src/electron/frontend/core/notifications.ts similarity index 82% rename from src/electron/frontend/core/dependencies.js rename to src/electron/frontend/core/notifications.ts index 810456d11..261007df0 100644 --- a/src/electron/frontend/core/dependencies.js +++ b/src/electron/frontend/core/notifications.ts @@ -1,24 +1,4 @@ import { Notyf } from "notyf"; -import checkChromatic from "chromatic/isChromatic"; -import lottie from "lottie-web"; - -// ---------- Lottie Helper ---------- -const isChromatic = checkChromatic(); - -export const startLottie = (lottieElement, animationData) => { - lottieElement.innerHTML = ""; - const thisLottie = lottie.loadAnimation({ - container: lottieElement, - animationData, - renderer: "svg", - loop: !isChromatic, - autoplay: !isChromatic, - }); - - if (isChromatic) thisLottie.goToAndStop(thisLottie.getDuration(true) - 1, true); // Go to last frame - - return thisLottie; -}; const longDuration = 20000; diff --git a/src/electron/frontend/core/server/index.ts b/src/electron/frontend/core/server/index.ts index 89edce13a..4fbc898ad 100644 --- a/src/electron/frontend/core/server/index.ts +++ b/src/electron/frontend/core/server/index.ts @@ -5,7 +5,7 @@ import { isTestEnvironment } from '../globals.js' import { notyf, -} from '../dependencies.js' +} from '../notifications' import Swal from 'sweetalert2' diff --git a/src/electron/frontend/utils/random.ts b/src/electron/frontend/utils/random.ts index 04fcbc60e..12dec871c 100644 --- a/src/electron/frontend/utils/random.ts +++ b/src/electron/frontend/utils/random.ts @@ -1,7 +1,13 @@ +const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; export const getRandomIndex = (count: number) => Math.floor(count * Math.random()); -export const getRandomString = () => Math.random().toString(36).substring(7); +// return random string always of len characters +export const getRandomString = (len = 7) => { + let result = ""; + for (let i = 0; i < len; i++) result += chars.charAt(Math.floor(Math.random() * chars.length)); + return result; +} export const getRandomSample = ( array: any[], @@ -18,4 +24,4 @@ export const getRandomSample = ( result.push(element); } return result; -}; +}; diff --git a/tests/components/InstanceManager.test.ts b/tests/components/InstanceManager.test.ts new file mode 100644 index 000000000..c4a6388f7 --- /dev/null +++ b/tests/components/InstanceManager.test.ts @@ -0,0 +1,120 @@ +import { describe, expect, it } from "vitest"; +import { InstanceManager } from "../../src/electron/frontend/core/components/InstanceManager"; + +const createInstance = () => { + return true +} + +const singleInstance = { + instance1: createInstance +} + +const multipleInstances = { + instance1: createInstance, + instance2: createInstance, +} + +const categorizedInstances = { + category1: { + instance1: createInstance, + instance2: createInstance, + }, + category2: { + instance3: createInstance, + instance4: createInstance, + } +} + +describe('InstanceManager', () => { + + const mountComponent = async (props = {}) => { + const element = new InstanceManager(props); + document.body.appendChild(element); + await element.updateComplete; + return element + } + + + it('renders', async () => { + const element = await mountComponent({ instances: singleInstance }); + expect(element.shadowRoot.querySelector('#content')).to.exist; + expect(element.shadowRoot.querySelector('#instance-sidebar')).to.exist; + element.remove() + }); + + it('toggles input visibility', async () => { + const element = await mountComponent({ + instances: multipleInstances, + onAdded: () => ({ value: createInstance }) + }); + + const button = element.shadowRoot.querySelector('#add-new-button'); + const newInfoContainer = element.shadowRoot.querySelector('#new-info'); + const input = newInfoContainer.querySelector('input'); + const submitButton = newInfoContainer.querySelector('nwb-button'); + + expect(newInfoContainer.hidden).to.be.true; + + button.click(); + await element.updateComplete; + expect(newInfoContainer.hidden).to.be.false; + + input.value = 'newInstance'; + submitButton.click(); + await element.updateComplete; + + expect(newInfoContainer.hidden).to.be.true; + + // NOTE: Check for new instance + expect(element.instances['newInstance']).to.exist; + + element.remove() + }); + + it('updates state correctly', async () => { + const element = await mountComponent({ + instances: multipleInstances, + }); + + element.updateState('instance1', 'inactive'); + await element.updateComplete; + + const instance = element.getInstance('instance1') + + expect(instance.status).to.equal('inactive'); + element.remove() + }); + + it('selects instance on click', async () => { + const element = await mountComponent({ + instances: multipleInstances + }); + + const instance1 = element.getInstance('instance1'); + const instance2 = element.getInstance('instance2'); + instance2.click(); + await element.updateComplete; + + expect(element.shadowRoot.querySelector(`[data-instance=${instance1.id}]`).getAttribute('hidden')).to.be.null; + expect(element.shadowRoot.querySelector(`[data-instance=${instance2.id}]`).getAttribute('hidden')).to.exist; + + // instance2.getAttribute('selected')).to.exist; + // expect(instance1.getAttribute('selected')).to.be.null; + + element.remove() + }); + + it('renders accordion for categories', async () => { + const element = await mountComponent({ + instances: categorizedInstances + }); + + await element.updateComplete; + + const accordion1 = element.shadowRoot.querySelector('nwb-accordion[name="category1"]'); + const accordion2 = element.shadowRoot.querySelector('nwb-accordion[name="instance1"]'); + expect(accordion1).to.exist; + expect(accordion2).to.not.exist; + element.remove() + }); +}); \ No newline at end of file diff --git a/tests/components/forms.test.ts b/tests/components/forms.test.ts new file mode 100644 index 000000000..4ea8a260b --- /dev/null +++ b/tests/components/forms.test.ts @@ -0,0 +1,376 @@ +import { JSONSchemaForm } from '../../src/electron/frontend/core/components/JSONSchemaForm'; +import { describe, it, expect } from 'vitest'; + +import { validateOnChange } from "../../src/electron/frontend/core/validation/index.js"; +import { SimpleTable } from '../../src/electron/frontend/core/components/SimpleTable' + +import baseMetadataSchema from '../../src/schemas/base-metadata.schema' + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +const NWBFileSchemaProperties = baseMetadataSchema.properties.NWBFile.properties + +// Helper function to mount the component +async function mountComponent(props) { + + const form = new JSONSchemaForm(props); + + document.body.append(form) + await form.rendered + + return form; +} + +describe('JSONSchemaForm', () => { + it('renders text input correctly', async () => { + + const defaultValue = 'John Doe'; + const schema = { + properties: { + name: { type: 'string', title: 'Name', default: defaultValue }, + }, + }; + + const form = await mountComponent({ schema }); + await form.rendered + const nameInput = form.getFormElement('name'); + expect(nameInput).toBeDefined(); + expect(nameInput.value).toBe(defaultValue); + expect(form.resolved.name).toBe(defaultValue); + await nameInput.updateData('Jane Doe'); + expect(form.resolved.name).toBe('Jane Doe'); + }); + + it('renders number input correctly', async () => { + const schema = { + properties: { + age: { type: 'number', title: 'Age' }, + }, + }; + + const form = await mountComponent({ schema }); + const ageInput = form.getFormElement('age'); + expect(ageInput).toBeDefined(); + await ageInput.updateData(30); + expect(form.resolved.age).toBe(30); + }); + + it('renders boolean input correctly', async () => { + const schema = { + properties: { + active: { type: 'boolean', title: 'Active' }, + }, + }; + + const form = await mountComponent({ schema }); + const activeInput = form.getFormElement('active'); + expect(activeInput).toBeDefined(); + await activeInput.updateData(true); + expect(form.resolved.active).toBe(true); + }); + + + it('renders array input correctly', async () => { + const schema = { + properties: { + hobbies: { + type: 'array', + title: 'Hobbies', + items: { type: 'string' }, + }, + }, + }; + + const form = await mountComponent({ schema }); + const hobbies = form.getFormElement('hobbies'); + expect(hobbies).toBeDefined(); + expect(hobbies.value).toBeUndefined(); + await hobbies.updateData(['Reading']); + expect(form.resolved.hobbies).toEqual(['Reading']); + }); + + it('renders object input correctly', async () => { + const schema = { + properties: { + address: { + type: 'object', + title: 'Address', + properties: { + street: { type: 'string', title: 'Street' }, + city: { type: 'string', title: 'City' }, + zip: { type: 'string', title: 'ZIP Code' }, + }, + }, + }, + }; + + const form = await mountComponent({ schema }); + const streetInput = form.getFormElement(['address', 'street']); + expect(streetInput).toBeDefined(); + await streetInput.updateData('123 Main St'); + expect(form.resolved.address.street).toBe('123 Main St'); + }); + + it('renders enum input correctly', async () => { + const schema = { + properties: { + status: { + type: 'string', + title: 'Status', + enum: ['Active', 'Inactive', 'Pending'], + }, + }, + }; + + const form = await mountComponent({ schema }); + const input = form.getFormElement('status'); + expect(input).toBeDefined(); + await input.updateData('Inactive'); + expect(form.resolved.status).toBe('Inactive'); + }); + + it("renders tables correctly", async () => { + const schema = { + properties: { + users: { + type: 'array', + title: 'Users', + items: { + type: 'object', + properties: { + name: { type: 'string', title: 'Name' }, + age: { type: 'number', title: 'Age' }, + }, + }, + }, + }, + }; + + const form = await mountComponent({ + schema, + renderTable: (name, metadata, path) => { + if (name !== "Electrodes") return new SimpleTable(metadata); + else return true + }, + }); + + const users = form.getFormElement('users'); + expect(users).toBeDefined(); + await users.addRow(); + expect(users.data).toHaveLength(1); + + const row = users.getRow(0) + const newData = { name: 'John Doe', age: 30 } + await Promise.all(Object.entries(newData).map(([key, value]) => { + const cell = row.find(cell => cell.simpleTableInfo.col === key) + return cell.setInput(value) + })) + + await sleep(100) // Wait for updates to register on the table + + expect(form.resolved.users).toEqual([{ name: 'John Doe', age: 30 }]); + }) + + it('validates form correctly', async () => { + const schema = { + properties: { + name: { type: 'string', title: 'Name' }, + }, + required: ['name'], + }; + + const form = await mountComponent({ schema }); + const nameInput = form.getFormElement('name'); + expect(nameInput).toBeDefined(); + + let errors = false; + await form.validate().catch(() => errors = true); + expect(errors).toBe(true); + + await nameInput.updateData('John Doe'); + + await form.validate() + .then(() => errors = false) + .catch(() => errors = true); + + expect(errors).toBe(false); + expect(form.resolved.name).toBe('John Doe'); + + }); + + // Pop-up inputs and forms work correctly + it('creates a pop-up that submits properly', async () => { + + // Create the form + const form = new JSONSchemaForm({ + schema: { + "type": "object", + "required": ["keywords", "experimenter"], + "properties": { + "keywords": NWBFileSchemaProperties.keywords, + "experimenter": NWBFileSchemaProperties.experimenter + } + }, + }) + + document.body.append(form) + + await form.rendered + + // Validate that the results are incorrect + let errors = false + await form.validate().catch(() => errors = true) + expect(errors).toBe(true) // Is invalid + + + // Validate that changes to experimenter are valid + const experimenterInput = form.getFormElement(['experimenter']) + const experimenterButton = experimenterInput.shadowRoot.querySelector('nwb-button') + const experimenterModal = experimenterButton.onClick() + const experimenterNestedElement = experimenterModal.children[0].children[0] + const experimenterSubmitButton = experimenterModal.footer + + await sleep(1000) + + let modalFailed + try { + await experimenterSubmitButton.onClick() + modalFailed = false + } catch (e) { + modalFailed = true + } + + expect(modalFailed).toBe(true) // Is invalid + + await experimenterNestedElement.updateData(['first_name'], 'Garrett') + await experimenterNestedElement.updateData(['last_name'], 'Flynn') + + experimenterNestedElement.requestUpdate() + + await experimenterNestedElement.rendered + + try { + await experimenterSubmitButton.onClick() + modalFailed = false + } catch (e) { + modalFailed = true + } + + expect(modalFailed).toBe(false) // Is valid + + // Validate that changes to keywords are valid + const keywordsInput = form.getFormElement(['keywords']) + const input = keywordsInput.shadowRoot.querySelector('input') + const submitButton = keywordsInput.shadowRoot.querySelector('nwb-button') + const list = keywordsInput.shadowRoot.querySelector('nwb-list') + expect(list.items.length).toBe(0) // No items + + input.value = 'test' + await submitButton.onClick() + + expect(list.items.length).toBe(1) // Has item + expect(input.value).toBe('') // Input is cleared + + // Validate that the new structure is correct + const hasErrors = await form.validate(form.results).then(res => false).catch(() => true) + + expect(hasErrors).toBe(false) // Is valid + }) + + // TODO: Convert an integration + it('triggers and resolves inter-table updates correctly', async () => { + + const results = { + Ecephys: { // NOTE: This layer is required to place the properties at the right level for the hardcoded validation function + ElectrodeGroup: [{ name: 's1' }], + Electrodes: [{ group_name: 's1' }] + } + } + + const schema = { + properties: { + Ecephys: { + properties: { + ElectrodeGroup: { + type: "array", + items: { + required: ["name"], + properties: { + name: { + type: "string" + }, + }, + type: "object", + }, + }, + Electrodes: { + type: "array", + items: { + type: "object", + properties: { + group_name: { + type: "string", + }, + }, + } + }, + } + } + } + } + + + + // Add invalid electrode + const randomStringId = Math.random().toString(36).substring(7) + results.Ecephys.Electrodes.push({ group_name: randomStringId }) + + // Create the form + const form = new JSONSchemaForm({ + schema, + results, + validateOnChange, + renderTable: (name, metadata, path) => { + if (name !== "Electrodes") return new SimpleTable(metadata); + else return true + }, + }) + + document.body.append(form) + + await form.rendered + + // Validate that the results are incorrect + const errors = await form.validate().catch(() => true).catch((e) => e) + expect(errors).toBe(true) // Is invalid + + // Update the table with the missing electrode group + const table = form.getFormElement(['Ecephys', 'ElectrodeGroup']) // This is a SimpleTable where rows can be added + const idx = await table.addRow() + + const row = table.getRow(idx) + + const baseRow = table.getRow(0) + row.forEach((cell, i) => { + if (cell.simpleTableInfo.col === 'name') cell.setInput(randomStringId) // Set name to random string id + else cell.setInput(baseRow[i].value) // Otherwise carry over info + }) + + await sleep(1000) // Wait for the ElectrodeGroup table to update properly + form.requestUpdate() // Re-render the form to update the Electrodes table + + await form.rendered // Wait for the form to re-render and validate properly + + // Validate that the new structure is correct + const hasErrors = await form.validate().then(() => false).catch((e) => true) + + expect(hasErrors).toBe(false) // Is valid + + }) + + + +}); + diff --git a/tests/metadata.test.ts b/tests/metadata.test.ts index 66b8c91d9..a37c15242 100644 --- a/tests/metadata.test.ts +++ b/tests/metadata.test.ts @@ -8,18 +8,10 @@ import { createMockGlobalState } from './utils' import { Validator } from 'jsonschema' import { updateResultsFromSubjects } from '../src/electron/frontend/utils/data' -import { JSONSchemaForm } from '../src/electron/frontend/core/components/JSONSchemaForm' -import { validateOnChange } from "../src/electron/frontend/core/validation/index.js"; -import { SimpleTable } from '../src/electron/frontend/core/components/SimpleTable' - -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} var validator = new Validator(); -const NWBFileSchemaProperties = baseMetadataSchema.properties.NWBFile.properties describe('metadata is specified correctly', () => { @@ -44,175 +36,4 @@ test('removing all existing sessions will maintain the related subject entry on updateResultsFromSubjects(results, subjects) expect(Object.keys(results)).toEqual(Object.keys(copy)) -}) - -const popupSchemas = { - "type": "object", - "required": ["keywords", "experimenter"], - "properties": { - "keywords": NWBFileSchemaProperties.keywords, - "experimenter": NWBFileSchemaProperties.experimenter - } -} - -// Pop-up inputs and forms work correctly -test('pop-up inputs work correctly', async () => { - - const results = {} - - // Create the form - const form = new JSONSchemaForm({ schema: popupSchemas, results }) - - document.body.append(form) - - await form.rendered - - // Validate that the results are incorrect - let errors = false - await form.validate().catch(() => errors = true) - expect(errors).toBe(true) // Is invalid - - - // Validate that changes to experimenter are valid - const experimenterInput = form.getFormElement(['experimenter']) - const experimenterButton = experimenterInput.shadowRoot.querySelector('nwb-button') - const experimenterModal = experimenterButton.onClick() - const experimenterNestedElement = experimenterModal.children[0].children[0] - const experimenterSubmitButton = experimenterModal.footer - - await sleep(1000) - - let modalFailed - try { - await experimenterSubmitButton.onClick() - modalFailed = false - } catch (e) { - modalFailed = true - } - - expect(modalFailed).toBe(true) // Is invalid - - experimenterNestedElement.updateData(['first_name'], 'Garrett') - experimenterNestedElement.updateData(['last_name'], 'Flynn') - - experimenterNestedElement.requestUpdate() - - await experimenterNestedElement.rendered - - try { - await experimenterSubmitButton.onClick() - modalFailed = false - } catch (e) { - modalFailed = true - } - - expect(modalFailed).toBe(false) // Is valid - - // Validate that changes to keywords are valid - const keywordsInput = form.getFormElement(['keywords']) - const input = keywordsInput.shadowRoot.querySelector('input') - const submitButton = keywordsInput.shadowRoot.querySelector('nwb-button') - const list = keywordsInput.shadowRoot.querySelector('nwb-list') - expect(list.items.length).toBe(0) // No items - - input.value = 'test' - await submitButton.onClick() - - expect(list.items.length).toBe(1) // Has item - expect(input.value).toBe('') // Input is cleared - - // Validate that the new structure is correct - const hasErrors = await form.validate(form.results).then(res => false).catch(() => true) - - expect(hasErrors).toBe(false) // Is valid -}) - -// TODO: Convert an integration -test('inter-table updates are triggered', async () => { - - const results = { - Ecephys: { // NOTE: This layer is required to place the properties at the right level for the hardcoded validation function - ElectrodeGroup: [{ name: 's1' }], - Electrodes: [{ group_name: 's1' }] - } - } - - const schema = { - properties: { - Ecephys: { - properties: { - ElectrodeGroup: { - type: "array", - items: { - required: ["name"], - properties: { - name: { - type: "string" - }, - }, - type: "object", - }, - }, - Electrodes: { - type: "array", - items: { - type: "object", - properties: { - group_name: { - type: "string", - }, - }, - } - }, - } - } - } - } - - - - // Add invalid electrode - const randomStringId = Math.random().toString(36).substring(7) - results.Ecephys.Electrodes.push({ group_name: randomStringId }) - - // Create the form - const form = new JSONSchemaForm({ - schema, - results, - validateOnChange, - renderTable: (name, metadata, path) => { - if (name !== "Electrodes") return new SimpleTable(metadata); - else return true - }, - }) - - document.body.append(form) - - await form.rendered - - // Validate that the results are incorrect - const errors = await form.validate().catch(() => true).catch((e) => e) - expect(errors).toBe(true) // Is invalid - - // Update the table with the missing electrode group - const table = form.getFormElement(['Ecephys', 'ElectrodeGroup']) // This is a SimpleTable where rows can be added - const idx = await table.addRow() - - const row = table.getRow(idx) - - const baseRow = table.getRow(0) - row.forEach((cell, i) => { - if (cell.simpleTableInfo.col === 'name') cell.setInput(randomStringId) // Set name to random string id - else cell.setInput(baseRow[i].value) // Otherwise carry over info - }) - - await sleep(1000) // Wait for the ElectrodeGroup table to update properly - form.requestUpdate() // Re-render the form to update the Electrodes table - - await form.rendered // Wait for the form to re-render and validate properly - - // Validate that the new structure is correct - const hasErrors = await form.validate().then(() => false).catch((e) => true) - - expect(hasErrors).toBe(false) // Is valid -}) +}) \ No newline at end of file diff --git a/tests/utils.test.ts b/tests/utils.test.ts index a05d2fd6f..c53dba8f8 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -372,9 +372,9 @@ describe('Randomization Utilities', () => { }); it('should return a string of expected length', () => { - const result = random.getRandomString(); - expect(result.length).toBeGreaterThanOrEqual(5); // Length might vary slightly due to the nature of random - expect(result.length).toBeLessThanOrEqual(11); // Usually the length is around 7-10 characters + const len = 10; + const result = random.getRandomString(len); + expect(result.length).toEqual(len); // Length is always the specified length }); it('should return a different string each time it is called', () => { diff --git a/vite.config.js b/vite.config.js index e79e9e873..f8956daf4 100644 --- a/vite.config.js +++ b/vite.config.js @@ -27,13 +27,6 @@ export default defineConfig({ "src/electron/frontend/utils/electron.ts", "src/electron/frontend/utils/auto-update.ts", - // High-Level App Configuration - "src/electron/frontend/core/index.ts", - "src/electron/frontend/core/pages.js", - "src/electron/frontend/core/dependencies.js", - "src/electron/frontend/core/globals.js", - "src/electron/frontend/core/errors.ts", - // Server Communication "src/electron/frontend/core/server", "src/electron/frontend/utils/run.ts", @@ -42,10 +35,32 @@ export default defineConfig({ // Pure Native Rendering Interaction "src/electron/frontend/utils/table.ts", + // High-Level App Configuration + "src/electron/frontend/core/index.ts", + "src/electron/frontend/core/pages.js", + "src/electron/frontend/core/notifications.ts", + "src/electron/frontend/core/lotties.ts", + "src/electron/frontend/core/globals.js", + "src/electron/frontend/core/errors.ts", + + + // Dashboard-Related Components + "src/electron/frontend/core/components/Dashboard.js", + "src/electron/frontend/core/components/Main.js", + "src/electron/frontend/core/components/Footer.js", + "src/electron/frontend/core/components/NavigationSidebar.js", + "src/electron/frontend/core/components/StatusBar.js", + "src/electron/frontend/core/components/sidebar.js", + + // Just rendering + "src/electron/frontend/core/components/CodeBlock.js", + // Unclear how to test "src/electron/frontend/utils/popups.ts", "src/electron/frontend/utils/download.ts", "src/electron/frontend/utils/upload.ts", + "src/electron/frontend/core/components/FileSystemSelector.js", // Uses Electron dialog + ], }, }, From 04e0fa0ca8d0bdf5ee3d4a247cdecdd3f551e6b5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 18:19:23 +0000 Subject: [PATCH 06/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../core/components/JSONSchemaInput.js | 2 +- src/electron/frontend/core/lotties.ts | 2 +- tests/components/InstanceManager.test.ts | 28 +++++++++---------- tests/components/forms.test.ts | 3 +- tests/metadata.test.ts | 2 +- vite.config.js | 2 -- 6 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/electron/frontend/core/components/JSONSchemaInput.js b/src/electron/frontend/core/components/JSONSchemaInput.js index 84f72db08..91ec60014 100644 --- a/src/electron/frontend/core/components/JSONSchemaInput.js +++ b/src/electron/frontend/core/components/JSONSchemaInput.js @@ -606,7 +606,7 @@ export class JSONSchemaInput extends LitElement { this.#updateData(fullPath, value); const possiblePromise = this.#triggerValidation(name, path); - return resolve(possiblePromise, () => true) + return resolve(possiblePromise, () => true); } getElement = () => this.shadowRoot.querySelector(".schema-input"); diff --git a/src/electron/frontend/core/lotties.ts b/src/electron/frontend/core/lotties.ts index b5969254f..a29418ebd 100644 --- a/src/electron/frontend/core/lotties.ts +++ b/src/electron/frontend/core/lotties.ts @@ -16,4 +16,4 @@ export const startLottie = (lottieElement: HTMLElement, animationData: any) => { if (isChromatic) thisLottie.goToAndStop(thisLottie.getDuration(true) - 1, true); // Go to last frame return thisLottie; -}; \ No newline at end of file +}; diff --git a/tests/components/InstanceManager.test.ts b/tests/components/InstanceManager.test.ts index c4a6388f7..7a52b8de4 100644 --- a/tests/components/InstanceManager.test.ts +++ b/tests/components/InstanceManager.test.ts @@ -6,7 +6,7 @@ const createInstance = () => { } const singleInstance = { - instance1: createInstance + instance1: createInstance } const multipleInstances = { @@ -33,32 +33,32 @@ describe('InstanceManager', () => { await element.updateComplete; return element } - - + + it('renders', async () => { const element = await mountComponent({ instances: singleInstance }); expect(element.shadowRoot.querySelector('#content')).to.exist; expect(element.shadowRoot.querySelector('#instance-sidebar')).to.exist; element.remove() }); - + it('toggles input visibility', async () => { const element = await mountComponent({ instances: multipleInstances, onAdded: () => ({ value: createInstance }) }); - + const button = element.shadowRoot.querySelector('#add-new-button'); const newInfoContainer = element.shadowRoot.querySelector('#new-info'); const input = newInfoContainer.querySelector('input'); const submitButton = newInfoContainer.querySelector('nwb-button'); expect(newInfoContainer.hidden).to.be.true; - + button.click(); await element.updateComplete; expect(newInfoContainer.hidden).to.be.false; - + input.value = 'newInstance'; submitButton.click(); await element.updateComplete; @@ -70,7 +70,7 @@ describe('InstanceManager', () => { element.remove() }); - + it('updates state correctly', async () => { const element = await mountComponent({ instances: multipleInstances, @@ -80,11 +80,11 @@ describe('InstanceManager', () => { await element.updateComplete; const instance = element.getInstance('instance1') - + expect(instance.status).to.equal('inactive'); element.remove() }); - + it('selects instance on click', async () => { const element = await mountComponent({ instances: multipleInstances @@ -97,24 +97,24 @@ describe('InstanceManager', () => { expect(element.shadowRoot.querySelector(`[data-instance=${instance1.id}]`).getAttribute('hidden')).to.be.null; expect(element.shadowRoot.querySelector(`[data-instance=${instance2.id}]`).getAttribute('hidden')).to.exist; - + // instance2.getAttribute('selected')).to.exist; // expect(instance1.getAttribute('selected')).to.be.null; element.remove() }); - + it('renders accordion for categories', async () => { const element = await mountComponent({ instances: categorizedInstances }); await element.updateComplete; - + const accordion1 = element.shadowRoot.querySelector('nwb-accordion[name="category1"]'); const accordion2 = element.shadowRoot.querySelector('nwb-accordion[name="instance1"]'); expect(accordion1).to.exist; expect(accordion2).to.not.exist; element.remove() }); -}); \ No newline at end of file +}); diff --git a/tests/components/forms.test.ts b/tests/components/forms.test.ts index 4ea8a260b..a609f1698 100644 --- a/tests/components/forms.test.ts +++ b/tests/components/forms.test.ts @@ -172,7 +172,7 @@ describe('JSONSchemaForm', () => { expect(form.resolved.users).toEqual([{ name: 'John Doe', age: 30 }]); }) - + it('validates form correctly', async () => { const schema = { properties: { @@ -373,4 +373,3 @@ describe('JSONSchemaForm', () => { }); - diff --git a/tests/metadata.test.ts b/tests/metadata.test.ts index a37c15242..e3bf6e764 100644 --- a/tests/metadata.test.ts +++ b/tests/metadata.test.ts @@ -36,4 +36,4 @@ test('removing all existing sessions will maintain the related subject entry on updateResultsFromSubjects(results, subjects) expect(Object.keys(results)).toEqual(Object.keys(copy)) -}) \ No newline at end of file +}) diff --git a/vite.config.js b/vite.config.js index f8956daf4..5667e299b 100644 --- a/vite.config.js +++ b/vite.config.js @@ -42,7 +42,6 @@ export default defineConfig({ "src/electron/frontend/core/lotties.ts", "src/electron/frontend/core/globals.js", "src/electron/frontend/core/errors.ts", - // Dashboard-Related Components "src/electron/frontend/core/components/Dashboard.js", @@ -60,7 +59,6 @@ export default defineConfig({ "src/electron/frontend/utils/download.ts", "src/electron/frontend/utils/upload.ts", "src/electron/frontend/core/components/FileSystemSelector.js", // Uses Electron dialog - ], }, }, From 446801843b1f6b9e97e131751a1a14af40e7aec3 Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Fri, 14 Jun 2024 14:20:06 -0400 Subject: [PATCH 07/10] try this --- .github/workflows/daily_tests.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily_tests.yml b/.github/workflows/daily_tests.yml index 32b8b1edd..72d10f678 100644 --- a/.github/workflows/daily_tests.yml +++ b/.github/workflows/daily_tests.yml @@ -40,9 +40,11 @@ jobs: runs-on: ubuntu-latest needs: [DevTests, LiveServices, BuildTests, ExampleDataCache, ExampleDataTests] if: | - ${{ - always() && (needs.DevTests.result == 'failure' || needs.LiveServices.result == 'failure' || needs.BuildTests.result == 'failure' || needs.ExampleDataCache.result == 'failure' || needs.ExampleDataTests.result == 'failure') - }} + ${{ needs.DevTests.result }} == 'failure' || + ${{ needs.LiveServices.result }} == 'failure' || + ${{ needs.BuildTests.result }} == 'failure' || + ${{ needs.ExampleDataCache.result }} == 'failure' || + ${{ needs.ExampleDataTests.result }} == 'failure' steps: - name: Printout which failed run: | From 5aa5cd488a75795451a2b1de61e4473ecf7f9d53 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 18:35:21 +0000 Subject: [PATCH 08/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .github/workflows/daily_tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily_tests.yml b/.github/workflows/daily_tests.yml index 72d10f678..9261e5837 100644 --- a/.github/workflows/daily_tests.yml +++ b/.github/workflows/daily_tests.yml @@ -41,9 +41,9 @@ jobs: needs: [DevTests, LiveServices, BuildTests, ExampleDataCache, ExampleDataTests] if: | ${{ needs.DevTests.result }} == 'failure' || - ${{ needs.LiveServices.result }} == 'failure' || - ${{ needs.BuildTests.result }} == 'failure' || - ${{ needs.ExampleDataCache.result }} == 'failure' || + ${{ needs.LiveServices.result }} == 'failure' || + ${{ needs.BuildTests.result }} == 'failure' || + ${{ needs.ExampleDataCache.result }} == 'failure' || ${{ needs.ExampleDataTests.result }} == 'failure' steps: - name: Printout which failed From 9a77e83c3346e3ffd5891cf8d3613daf6166a6b3 Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Fri, 14 Jun 2024 15:11:04 -0400 Subject: [PATCH 09/10] debugging --- .github/workflows/daily_tests.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/daily_tests.yml b/.github/workflows/daily_tests.yml index 9261e5837..26aede375 100644 --- a/.github/workflows/daily_tests.yml +++ b/.github/workflows/daily_tests.yml @@ -39,12 +39,7 @@ jobs: NotifyOnAnyFailure: runs-on: ubuntu-latest needs: [DevTests, LiveServices, BuildTests, ExampleDataCache, ExampleDataTests] - if: | - ${{ needs.DevTests.result }} == 'failure' || - ${{ needs.LiveServices.result }} == 'failure' || - ${{ needs.BuildTests.result }} == 'failure' || - ${{ needs.ExampleDataCache.result }} == 'failure' || - ${{ needs.ExampleDataTests.result }} == 'failure' + if: ${{ needs.DevTests.result }} == 'failure' || ${{ needs.LiveServices.result }} == 'failure' || ${{ needs.BuildTests.result }} == 'failure' || ${{ needs.ExampleDataCache.result }} == 'failure' || ${{ needs.ExampleDataTests.result }} == 'failure' steps: - name: Printout which failed run: | @@ -53,6 +48,10 @@ jobs: echo "BuildTests: ${{ needs.BuildTests.result }}" echo "ExampleDataCache: ${{ needs.ExampleDataCache.result }}" echo "ExampleDataTests: ${{ needs.ExampleDataTests.result }}" + - name: debugging + run: export test=(${{ needs.DevTests.result }} == 'failure' || ${{ needs.LiveServices.result }} == 'failure' || ${{ needs.BuildTests.result }} == 'failure' || ${{ needs.ExampleDataCache.result }} == 'failure' || ${{ needs.ExampleDataTests.result }} == 'failure') + - name: debugging print + run: echo $test #- name: Printout logic trigger # run: ${{ needs.DevTests.result }} == 'failure' || ${{ needs.LiveServices.result }} == 'failure' || ${{ needs.BuildTests.result }} == 'failure' || ${{ needs.ExampleDataCache.result }} == 'failure' || ${{ needs.ExampleDataTests.result }} == 'failure' - uses: dawidd6/action-send-mail@v3 From dd24932eaf1c7b03a852a7e0ed3ecc0739f2fff7 Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Fri, 14 Jun 2024 15:42:07 -0400 Subject: [PATCH 10/10] debugging --- .github/workflows/daily_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily_tests.yml b/.github/workflows/daily_tests.yml index 26aede375..f3e2d5596 100644 --- a/.github/workflows/daily_tests.yml +++ b/.github/workflows/daily_tests.yml @@ -39,7 +39,7 @@ jobs: NotifyOnAnyFailure: runs-on: ubuntu-latest needs: [DevTests, LiveServices, BuildTests, ExampleDataCache, ExampleDataTests] - if: ${{ needs.DevTests.result }} == 'failure' || ${{ needs.LiveServices.result }} == 'failure' || ${{ needs.BuildTests.result }} == 'failure' || ${{ needs.ExampleDataCache.result }} == 'failure' || ${{ needs.ExampleDataTests.result }} == 'failure' + if: always() && (${{ needs.DevTests.result }} == 'failure' || ${{ needs.LiveServices.result }} == 'failure' || ${{ needs.BuildTests.result }} == 'failure' || ${{ needs.ExampleDataCache.result }} == 'failure' || ${{ needs.ExampleDataTests.result }} == 'failure') steps: - name: Printout which failed run: |