diff --git a/.github/workflows/auto-testing-label.yml b/.github/workflows/auto-testing-label.yml index 57dede583cf..6c6fc1002a0 100644 --- a/.github/workflows/auto-testing-label.yml +++ b/.github/workflows/auto-testing-label.yml @@ -20,24 +20,37 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const pr = context.payload.pull_request; - const isDraft = pr.draft; - const isReadyForTestingComment = context.payload.comment?.body.includes('ready for testing'); - const isChangesRequired = context.payload.review?.state === 'changes_requested'; + const comment = context.payload.comment; + const review = context.payload.review; - if ((isReadyForTestingComment && !isDraft) || (!isDraft && pr.draft_changed)) { - await github.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - labels: ['needs testing'] - }); - } + if (pr) { + const isDraft = pr.draft; + const isReadyForTestingComment = comment && comment.body.includes('ready for testing'); + const isChangesRequired = review && review.state === 'changes_requested'; + + if ((isReadyForTestingComment && !isDraft) || (!isDraft && pr.draft_changed)) { + await github.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + labels: ['needs testing'] + }); + } - if (isChangesRequired) { - await github.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: 'Reminder: To add the "needs testing" label, comment "ready for testing" on this PR.' - }); + if (isChangesRequired) { + await github.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + body: 'Reminder: To add the "needs testing" label, comment "ready for testing" on this PR.' + }); + if (pr.labels.some(label => label.name === 'needs testing')) { + await github.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + name: 'needs testing' + }); + } + } } diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index 31881f11af5..5e671055535 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -6,7 +6,7 @@ on: pull_request: branches: - develop - - master + - staging workflow_dispatch: jobs: diff --git a/.storybook/vite.config.mts b/.storybook/vite.config.mts index 1e6e2445ffa..89e6222f220 100644 --- a/.storybook/vite.config.mts +++ b/.storybook/vite.config.mts @@ -1,8 +1,2 @@ -import { defineConfig } from "vite"; - -export default defineConfig({ - esbuild: { - loader: "tsx", - include: [/src\/.*\.[tj]sx?$/, /.storybook\/.*\.[tj]sx?$/], - }, -}); +/** @type {import('vite').UserConfig} */ +export default {}; diff --git a/README.md b/README.md index f2477e512f7..de0b06bd63e 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,13 @@ Authenticate to staging API with any of the following credentials - Once the code review is done, the PR will be marked with a "Needs Testing" label where it'll be queued for QA testing. - Once tested, the PR would be marked with a "Tested" label and would be queued for merge. +### Testing + +To ensure the quality of our pull requests, we use a variety of tools: + +- **Automated E2E Testing:** We use Cypress for end-to-end testing to automatically verify the functionality and performance of our code. +- **Manual Real Device Testing:** We use BrowserStack to manually test our code on real devices, ensuring compatibility and functionality across different platforms and browsers. + #### ๐Ÿงช Run cypress tests To run cypress tests locally, you'll need to setup the backend to run locally and load dummy data required for cypress to the database. See [docs](https://github.com/coronasafe/care#self-hosting). @@ -110,6 +117,7 @@ npm run cypress:open # To debug and run tests individually. - [CARE Documentation](https://docs.coronasafe.network/coronasafe-care-documentation/) - [Swagger API Documentation](https://careapi.ohc.network/swagger/) - [Storybook component library](https://careui.coronasafe.in/) +- [Testing Documentation](https://docs.coronasafe.network/care-testing-documentation/) ## ๐Ÿš€ Production diff --git a/cypress/e2e/facility_spec/facility_manage.cy.ts b/cypress/e2e/facility_spec/facility_manage.cy.ts index 6aa0f85b282..d317b222078 100644 --- a/cypress/e2e/facility_spec/facility_manage.cy.ts +++ b/cypress/e2e/facility_spec/facility_manage.cy.ts @@ -8,6 +8,7 @@ describe("Facility Manage Functions", () => { const loginPage = new LoginPage(); const facilityManage = new FacilityManage(); const facilityPage = new FacilityPage(); + const facilityName = "Dummy Facility 40"; const facilityMiddlewareUpdateButton = "Update"; const facilityMiddleware = "dev-middleware.coronasafe.live"; const facilityUpdatedMiddleware = "updated.coronasafe.live"; @@ -35,6 +36,8 @@ describe("Facility Manage Functions", () => { cy.restoreLocalStorage(); cy.clearLocalStorage(/filters--.+/); cy.awaitUrl("/"); + facilityPage.typeFacilitySearch(facilityName); + facilityPage.verifyFacilityBadgeContent(facilityName); facilityPage.visitAlreadyCreatedFacility(); }); diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts index 8a1b31e3e46..328719f5e90 100644 --- a/cypress/pageobject/Facility/FacilityCreation.ts +++ b/cypress/pageobject/Facility/FacilityCreation.ts @@ -10,6 +10,11 @@ class FacilityPage { .should("eq", 200); } + typeFacilitySearch(facilityName) { + cy.get("#search").click().clear(); + cy.get("#search").click().type(facilityName); + } + visitUpdateFacilityPage(url: string) { cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities"); cy.visit(url); @@ -292,8 +297,8 @@ class FacilityPage { } selectLocation(location: string) { + cy.intercept("https://maps.googleapis.com/**").as("mapApi"); cy.get("span > svg.care-svg-icon__baseline.care-l-map-marker").click(); - cy.intercept("https://maps.googleapis.com/maps/api/mapsjs/*").as("mapApi"); cy.wait("@mapApi").its("response.statusCode").should("eq", 200); cy.get("input#pac-input").type(location).type("{enter}"); cy.wait(2000); diff --git a/src/Common/hooks/useMSEplayer.ts b/src/Common/hooks/useMSEplayer.ts index 39e4d356910..127fe94b9a0 100644 --- a/src/Common/hooks/useMSEplayer.ts +++ b/src/Common/hooks/useMSEplayer.ts @@ -106,6 +106,11 @@ export const useMSEMediaPlayer = ({ if (!mseSourceBuffer.updating) { if (mseQueue.length > 0) { const packet = mseQueue.shift(); + // Check if SourceBuffer has been removed before appending buffer + if (mseSourceBuffer.removed) { + console.error("Attempted to append to a removed SourceBuffer."); + return; + } mseSourceBuffer.appendBuffer(packet); } else { mseStreamingStarted = false; @@ -122,6 +127,11 @@ export const useMSEMediaPlayer = ({ const readPacket = (packet: any) => { if (!mseStreamingStarted) { + // Check if SourceBuffer has been removed before appending buffer + if (mseSourceBuffer.removed) { + console.error("Attempted to append to a removed SourceBuffer."); + return; + } mseSourceBuffer.appendBuffer(packet); mseStreamingStarted = true; return; diff --git a/src/Components/Common/BloodPressureFormField.tsx b/src/Components/Common/BloodPressureFormField.tsx index 3f7ecacce46..95dbad02f62 100644 --- a/src/Components/Common/BloodPressureFormField.tsx +++ b/src/Components/Common/BloodPressureFormField.tsx @@ -11,7 +11,7 @@ import { BloodPressure } from "../Patient/models"; type Props = FormFieldBaseProps; export default function BloodPressureFormField(props: Props) { - const field = useFormFieldPropsResolver(props as any); + const field = useFormFieldPropsResolver(props); const handleChange = (event: FieldChangeEvent) => { const value: BloodPressure = { diff --git a/src/Components/Common/FilePreviewDialog.tsx b/src/Components/Common/FilePreviewDialog.tsx index b4443d330ea..3f55621e8c7 100644 --- a/src/Components/Common/FilePreviewDialog.tsx +++ b/src/Components/Common/FilePreviewDialog.tsx @@ -130,7 +130,7 @@ const FilePreviewDialog = (props: FilePreviewProps) => { file diff --git a/src/Components/Common/PMJAYProcedurePackageAutocomplete.tsx b/src/Components/Common/PMJAYProcedurePackageAutocomplete.tsx index f99e07ab5db..1fee5599220 100644 --- a/src/Components/Common/PMJAYProcedurePackageAutocomplete.tsx +++ b/src/Components/Common/PMJAYProcedurePackageAutocomplete.tsx @@ -17,7 +17,7 @@ type PMJAYPackageItem = { type Props = FormFieldBaseProps; export default function PMJAYProcedurePackageAutocomplete(props: Props) { - const field = useFormFieldPropsResolver(props as any); + const field = useFormFieldPropsResolver(props); const { fetchOptions, isLoading, options } = useAsyncOptions("code"); diff --git a/src/Components/Common/RouteToFacilitySelect.tsx b/src/Components/Common/RouteToFacilitySelect.tsx index 0bd4e83100a..56ba5c528dc 100644 --- a/src/Components/Common/RouteToFacilitySelect.tsx +++ b/src/Components/Common/RouteToFacilitySelect.tsx @@ -19,7 +19,7 @@ export const keys = Object.keys(ROUTE_TO_FACILITY_OPTIONS).map((key) => type Props = FormFieldBaseProps; export default function RouteToFacilitySelect(props: Props) { - const field = useFormFieldPropsResolver(props as any); + const field = useFormFieldPropsResolver(props); return ( & { }; export default function UserAutocompleteFormField(props: Props) { - const field = useFormFieldPropsResolver(props as any); + const field = useFormFieldPropsResolver(props); const { fetchOptions, isLoading, options } = useAsyncOptions( "id", { queryResponseExtractor: (data) => data.results }, @@ -65,6 +65,8 @@ export default function UserAutocompleteFormField(props: Props) {
-
+
{str(Belt.Float.toString(painScale))} +
color + }, + ), + (), + )}> + {str( + switch getLabels(Belt.Float.toInt(painScale)) { + | (label, _) => label + }, + )} +
{str("Pain Scale")}
@@ -159,7 +175,7 @@ let make = ( ?