diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7d37a17 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,91 @@ +name: Continuous Integration + +on: push + +jobs: + build_test: + name: Build and Run Jest Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + - name: Install Dependencies + run: npm ci + - name: Build + run: npm run build + - name: Run Tests + run: npm run test:coverage -- --runInBand + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + flags: jest + token: ${{ secrets.CODECOV_TOKEN }} + cypress: + runs-on: ubuntu-latest + strategy: + # when one test fails, DO NOT cancel the other + # containers, because this will kill Cypress processes + # leaving the Dashboard hanging ... + # https://github.com/cypress-io/github-action/issues/48 + fail-fast: false + matrix: + # run 3 copies of the current job in parallel + containers: [1, 2, 3] + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: cypress-io/github-action@v6 + with: + start: npm start + wait-on: 'http://localhost:8080' + # only record the results to dashboard.cypress.io if CYPRESS_RECORD_KEY is set + record: ${{ !!secrets.CYPRESS_RECORD_KEY }} + # only do parallel if we have a record key + parallel: ${{ !!secrets.CYPRESS_RECORD_KEY }} + env: + # pass the Dashboard record key as an environment variable + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + # pass GitHub token to allow accurately detecting a build vs a re-run build + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # turn on code coverage when running npm start + # so far we've been using a webpack coverage-istanbul-loader for this + # but there has been work on using the code coverage support in the browser directly, + # which should be much faster + CODE_COVERAGE: true + # Also turn on the code coverage tasks in cypress itself, these are disabled + # by default. + CYPRESS_coverage: true + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + flags: cypress + token: ${{ secrets.CODECOV_TOKEN }} + s3-deploy: + name: S3 Deploy + needs: + - build_test + - cypress + runs-on: ubuntu-latest + environment: + name: ${{ github.ref_type == 'branch' && 'branches' || 'versions' }} + url: https://models-resources.concord.org/codap-day-length-plugin-3/${{ steps.s3-deploy.outputs.deployPath }}/index.html + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + - name: Install Dependencies + run: npm ci + env: + # skip installing cypress since it isn't needed for just building + # This decreases the deploy time quite a bit + CYPRESS_INSTALL_BINARY: 0 + - uses: concord-consortium/s3-deploy-action@v1 + id: s3-deploy + with: + bucket: models-resources + prefix: codap-day-length-plugin-3 + awsAccessKeyId: ${{ secrets.AWS_ACCESS_KEY_ID }} + awsSecretAccessKey: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # Parameters to GHActions have to be strings, so a regular yaml array cannot + # be used. Instead the `|` turns the following lines into a string + topBranches: | + ["main"] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7aa39f9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,24 @@ +name: Release +on: + workflow_dispatch: + inputs: + version: + description: The git tag for the version to use for index.html + required: true +env: + BUCKET: models-resources + PREFIX: codap-day-length-plugin-3 + SRC_FILE: index-top.html + DEST_FILE: index.html +jobs: + release: + runs-on: ubuntu-latest + steps: + - run: > + aws s3 cp + s3://${{ env.BUCKET }}/${{ env.PREFIX }}/version/${{ github.event.inputs.version }}/${{ env.SRC_FILE }} + s3://${{ env.BUCKET }}/${{ env.PREFIX }}/${{ env.DEST_FILE }} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: us-east-1 diff --git a/__mocks__/fileMock.js b/__mocks__/fileMock.js new file mode 100644 index 0000000..86059f3 --- /dev/null +++ b/__mocks__/fileMock.js @@ -0,0 +1 @@ +module.exports = 'test-file-stub'; diff --git a/cypress/e2e/workspace.test.ts b/cypress/e2e/workspace.test.ts index 6cf6bed..dd6b84c 100644 --- a/cypress/e2e/workspace.test.ts +++ b/cypress/e2e/workspace.test.ts @@ -7,7 +7,8 @@ context("Test the overall app", () => { describe("Desktop functionalities", () => { it("renders with text", () => { - ae.getApp().should("have.text", "Hello World"); + // Check if the text includes "Location" + ae.getApp().should("contain.text", "Location"); }); }); }); diff --git a/cypress/support/elements/app-elements.ts b/cypress/support/elements/app-elements.ts index d620d7c..8d58211 100644 --- a/cypress/support/elements/app-elements.ts +++ b/cypress/support/elements/app-elements.ts @@ -1,5 +1,5 @@ export const AppElements = { getApp() { - return cy.get(".app"); + return cy.get(".App"); } }; diff --git a/package.json b/package.json index 0029b71..28c3528 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "moduleNameMapper": { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(css|less|sass)$": "identity-obj-proxy" + "\\.(css|less|sass|scss)$": "identity-obj-proxy" }, "moduleFileExtensions": [ "ts", diff --git a/src/components/App.test.tsx b/src/components/App.test.tsx deleted file mode 100755 index 051b351..0000000 --- a/src/components/App.test.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; -import { App } from "./App"; -import { render, screen } from "@testing-library/react"; - -describe("test load app", () => { - it("renders without crashing", () => { - render(); - expect(screen.getByText("CODAP Day Length Plugin 3")).toBeDefined(); - }); -}); - diff --git a/src/test/setupTests.ts b/src/test/setupTests.ts index d0de870..f190b0f 100644 --- a/src/test/setupTests.ts +++ b/src/test/setupTests.ts @@ -1 +1,11 @@ import "@testing-library/jest-dom"; +import $ from "jquery"; + +declare global { + interface Window { + $: typeof $; + jQuery: typeof $; + } +} + +window.$ = window.jQuery = $; diff --git a/src/utils/daylight-utils.test.ts b/src/utils/daylight-utils.test.ts new file mode 100644 index 0000000..09c1767 --- /dev/null +++ b/src/utils/daylight-utils.test.ts @@ -0,0 +1,19 @@ +import { getSunrayAngleInDegrees } from "./daylight-utils" + +describe("getSunrayAngleInDegrees", () => { + it("should return the correct sunray angle in degrees", () => { + const dayNum = 172; + const earthTilt = 23.5; + const lat = 37.7749; + const result = getSunrayAngleInDegrees(dayNum, earthTilt, lat); + expect(result).toBeCloseTo(75.7251); + }); + + it("should return the correct sunray angle in degrees (90deg case)", () => { + const dayNum = 171; + const earthTilt = 23.5; + const lat = 23.5; + const result = getSunrayAngleInDegrees(dayNum, earthTilt, lat); + expect(result).toBeCloseTo(90); + }); +});