Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b7680ff
test: add cypress tests for Quartz logos and documentation link
suvanbanerjee Apr 6, 2026
ac056d9
add dashboard mode test and change data-test to data-cy
suvanbanerjee Apr 14, 2026
b133c0f
add percy and code from feat/add-percy-for-visual-regression-testing
suvanbanerjee Apr 17, 2026
d249ab2
Merge branch 'development' into cypress-testing
suvanbanerjee Apr 28, 2026
0284661
feat: add cypress fixtures support and add script and commands for da…
suvanbanerjee Apr 30, 2026
a44e83e
add baseline managment and image comparison
suvanbanerjee May 6, 2026
43b7758
update fixtures
suvanbanerjee May 11, 2026
02f8251
make seperate snapshoot test for pvNational
suvanbanerjee May 11, 2026
dea2725
add snapshoot test for dashboard mode
suvanbanerjee May 11, 2026
f485e53
add snapshoot test for delta tab
suvanbanerjee May 11, 2026
ff02123
add snapshoot test for sites tab
suvanbanerjee May 11, 2026
7c51d88
feat: mock auth0 in cypress
suvanbanerjee Jun 22, 2026
9fdb92a
chore: disable ci flag for cypress webgl rendering
suvanbanerjee Jun 24, 2026
f4135b6
fix: change Cypress browser from chrome to electron in ci
suvanbanerjee Jun 24, 2026
121b0d0
chore: add diff commenting on pr in test
suvanbanerjee Jun 24, 2026
64da215
feat: new thresh with blur and test commit msg
suvanbanerjee Jun 24, 2026
cb238b2
reveret test string
suvanbanerjee Jun 24, 2026
0b47c82
Merge remote-tracking branch 'origin/development' into cypress-testing
suvanbanerjee Aug 8, 2026
4e88145
chore: update fixtures for p-level and sat ui
suvanbanerjee Aug 9, 2026
b59ab8b
feat: add cypress test for 7-plevels
suvanbanerjee Aug 9, 2026
e7ae1f5
feat: add cypress tests for satellite ui
suvanbanerjee Aug 9, 2026
26093e8
chore: update blur to 2 and cypress image snapshot
suvanbanerjee Aug 10, 2026
19cc40a
chore(cypress): skip sites tab snapshot test for now
suvanbanerjee Aug 12, 2026
5f68b09
fix(cypress): fix skip syntax
suvanbanerjee Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 44 additions & 25 deletions .github/workflows/yarn_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
yarn test --clear-cache --coverage --coverageDirectory=../..
- name: Run cypress tests
# Uses the official Cypress GitHub action https://github.com/cypress-io/github-action
id: cypress
uses: cypress-io/github-action@v6
with:
# Starts web server for E2E tests - replace with your own server invocation
Expand All @@ -49,30 +50,48 @@ jobs:
record: true
# parallel: true # Runs test in parallel using settings above
config: chromeWebSecurity=false
browser: chrome
env:
AUTH0_AUDIENCE: ${{ secrets.AUTH0_AUDIENCE }}
AUTH0_BASE_URL: ${{ secrets.AUTH0_BASE_URL }}
AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CLIENT_ID }}
AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CLIENT_SECRET }}
AUTH0_DOMAIN: ${{ secrets.AUTH0_DOMAIN }}
AUTH0_ISSUER_BASE_URL: ${{ secrets.AUTH0_ISSUER_BASE_URL }}
AUTH0_SCOPE: ${{ secrets.AUTH0_SCOPE }}
AUTH0_SECRET: ${{ secrets.AUTH0_SECRET }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}
GITHUB_TOKEN: ${{ secrets.CYPRESS_GITHUB_TOKEN }}
NEXT_PUBLIC_API_PREFIX: ${{ secrets.NEXT_PUBLIC_API_PREFIX }}
NEXT_PUBLIC_SITES_API_PREFIX: ${{ secrets.NEXT_PUBLIC_SITES_API_PREFIX }}
NEXT_PUBLIC_AUTH0_AUDIENCE: ${{ secrets.NEXT_PUBLIC_AUTH0_AUDIENCE }}
NEXT_PUBLIC_AUTH0_API_AUDIENCE: ${{ secrets.NEXT_PUBLIC_AUTH0_API_AUDIENCE }}
NEXT_PUBLIC_AUTH0_CLIENT_ID: ${{ secrets.NEXT_PUBLIC_AUTH0_CLIENT_ID }}
NEXT_PUBLIC_AUTH0_DOMAIN: ${{ secrets.NEXT_PUBLIC_AUTH0_DOMAIN }}
NEXT_PUBLIC_AUTH0_PASSWORD: ${{ secrets.NEXT_PUBLIC_AUTH0_PASSWORD }}
NEXT_PUBLIC_AUTH0_SCOPE: ${{ secrets.NEXT_PUBLIC_AUTH0_SCOPE }}
NEXT_PUBLIC_AUTH0_USERNAME: ${{ secrets.NEXT_PUBLIC_AUTH0_USERNAME }}
NEXT_PUBLIC_4H_VIEW: 'true'
NEXT_PUBLIC_CI: 'true'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
browser: electron
env:
AUTH0_AUDIENCE: ${{ secrets.AUTH0_AUDIENCE }}
AUTH0_BASE_URL: ${{ secrets.AUTH0_BASE_URL }}
AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CLIENT_ID }}
AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CLIENT_SECRET }}
AUTH0_DOMAIN: ${{ secrets.AUTH0_DOMAIN }}
AUTH0_ISSUER_BASE_URL: ${{ secrets.AUTH0_ISSUER_BASE_URL }}
AUTH0_SCOPE: ${{ secrets.AUTH0_SCOPE }}
AUTH0_SECRET: ${{ secrets.AUTH0_SECRET }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}
GITHUB_TOKEN: ${{ secrets.CYPRESS_GITHUB_TOKEN }}
NEXT_PUBLIC_API_PREFIX: ${{ secrets.NEXT_PUBLIC_API_PREFIX }}
NEXT_PUBLIC_SITES_API_PREFIX: ${{ secrets.NEXT_PUBLIC_SITES_API_PREFIX }}
NEXT_PUBLIC_AUTH0_AUDIENCE: ${{ secrets.NEXT_PUBLIC_AUTH0_AUDIENCE }}
NEXT_PUBLIC_AUTH0_API_AUDIENCE: ${{ secrets.NEXT_PUBLIC_AUTH0_API_AUDIENCE }}
NEXT_PUBLIC_AUTH0_CLIENT_ID: ${{ secrets.NEXT_PUBLIC_AUTH0_CLIENT_ID }}
NEXT_PUBLIC_AUTH0_DOMAIN: ${{ secrets.NEXT_PUBLIC_AUTH0_DOMAIN }}
NEXT_PUBLIC_AUTH0_PASSWORD: ${{ secrets.NEXT_PUBLIC_AUTH0_PASSWORD }}
NEXT_PUBLIC_AUTH0_SCOPE: ${{ secrets.NEXT_PUBLIC_AUTH0_SCOPE }}
NEXT_PUBLIC_AUTH0_USERNAME: ${{ secrets.NEXT_PUBLIC_AUTH0_USERNAME }}
NEXT_PUBLIC_4H_VIEW: 'true'
NEXT_PUBLIC_CI: 'false'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}

- name: Upload snapshot diffs
if: always() && steps.cypress.outcome == 'failure'
uses: actions/upload-artifact@v4
with:
name: cypress-snapshot-diffs
path: apps/nowcasting-app/cypress/snapshots/**/__diff_output__/*.png

- name: Comment diff link on PR
if: always() && steps.cypress.outcome == 'failure'
uses: actions/github-script@v7
with:
script: |
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `### ❌ Snapshot diff\nDownload **cypress-snapshot-diffs** from [this run's artifacts](${runUrl}) to view the diff images.`
});
28 changes: 28 additions & 0 deletions apps/nowcasting-app/.percy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
version: 2
percy:
useSystemProxy: false
skipBaseBuild: false
snapshot:
widths:
- 1280
minHeight: 720
percyCSS: ""
enableJavaScript: false
cliEnableJavaScript: true
disableShadowDOM: false
forceShadowAsLightDOM: false
responsiveSnapshotCapture: false
ignoreCanvasSerializationErrors: false
discovery:
allowedHostnames: []
disallowedHostnames: []
networkIdleTimeout: 100
scrollToBottom: false
captureMockedServiceWorker: false
retry: false
launchOptions:
closeBrowser: true
upload:
files: "**/*.{png,jpg,jpeg}"
ignore: ""
stripExtensions: false
5 changes: 4 additions & 1 deletion apps/nowcasting-app/components/button-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ interface IButtonGroup {

const ButtonGroup = ({ rightString }: IButtonGroup) => {
return (
<span className="relative z-0 w-full flex justify-end shadow-sm mx-0">
<span
data-cy="measuring-unit-button-group"
className="relative z-0 w-full flex justify-end shadow-sm mx-0"
>
<div className="absolute left-0 top-0 items-center px-3 py-[1px] font-extrabold dash:text-xl dash:tracking-wide dash:py-1 text-white bg-black">
{rightString}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const DeltaHeaderBlock: React.FC<{
>
<div className="flex">
<div
data-cy="delta-header-large-text"
className={`${svg ? "ml-7 dash:ml-8 " : ""}${
unit === "MW" ? " dash:3xl:text-4xl " : " dash:3xl:text-5xl "
}dash:text-3xl dash:leading-none text-2xl gap-0.5 dash:gap-0 dash:mb-0.5 items-start flex flex-col font-semibold leading-none text-center`}
Expand Down
8 changes: 4 additions & 4 deletions apps/nowcasting-app/components/charts/forecast-header/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ export const ForecastHeadlineFigure: React.FC<{
}`;
return (
<div
data-test="pvlive-ocf-headline-figure"
data-cy="forecast-header-large-text"
className="flex gap-3 items-center m-auto h-10 dash:h-14 justify-between"
>
<div className="flex flex-1 self-center items-center justify-center">
<div className={`flex items-center ${textSizeClasses}`}>
<div data-cy="forecast-header-text" className={`flex items-center ${textSizeClasses}`}>
<ForecastLabel
position={"middle"}
tip={
Expand Down Expand Up @@ -108,7 +108,7 @@ export const NextForecast: React.FC<{ pv: string; tip: string; time: string; col
}) => {
return (
<div
data-test="forecast-label-tooltip"
data-cy="forecast-label-tooltip"
className="flex gap-3 items-center m-auto h-10 dash:h-14 justify-between"
>
<ForecastLabel
Expand Down Expand Up @@ -165,7 +165,7 @@ const ForecastHeaderUI: React.FC<ForecastHeaderProps> = ({
}) => {
return (
<div
data-test="national-chart-header"
data-cy="national-chart-header"
className="flex flex-initial content-between bg-ocf-gray-800 h-auto mb-4"
>
<div className="text-white dash:3xl:text-5xl dash:2xl:text-4xl dash:xl:text-3xl dash:tracking-wide lg:text-2xl md:text-lg text-base font-black m-auto ml-5 flex justify-evenly">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ const ForecastHeaderGSP: FC<ForecastHeaderGSPProps> = ({
);
return (
<div className={`flex content-between bg-ocf-gray-800 h-12 mb-4 ${height}`}>
<div className="dash:xl:text-2xl dash:2xl:text-3xl dash:3xl:text-4xl text-white lg:text-xl md:text-lg text-lg font-black m-auto ml-5 flex justify-evenly">
<div
data-cy="gsp-header-title"
className="dash:xl:text-2xl dash:2xl:text-3xl dash:3xl:text-4xl text-white lg:text-xl md:text-lg text-lg font-black m-auto ml-5 flex justify-evenly"
>
{titleTooltipText.length ? (
<ForecastLabel className="" position={"left"} tip={titleTooltipContent}>
{title}
Expand Down
2 changes: 2 additions & 0 deletions apps/nowcasting-app/components/layout/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const HeaderLink: React.FC<HeaderLinkProps> = ({
view,
setViewFunc
}) => {
const dataCy = view ? `${view.toLowerCase().replace(/\s+/g, "-")}-tab` : undefined;
const computedClasses = classNames(
className || "",
disabled ? "text-gray-500 cursor-not-allowed" : "cursor-pointer hover:text-ocf-yellow-400",
Expand All @@ -52,6 +53,7 @@ const HeaderLink: React.FC<HeaderLinkProps> = ({
return (
<Menu.Item>
<a
data-cy={dataCy}
className={classNames(computedClasses, textColorClasses)}
onClick={() => {
if (!disabled) setViewFunc(view);
Expand Down
17 changes: 14 additions & 3 deletions apps/nowcasting-app/components/layout/header/settingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,17 @@ export const SettingsModal: React.FC<Props> = ({ isOpen, onClose }) => {

return (
<>
<div className="fixed inset-0 z-40 bg-black/40" onClick={onClose} />
<div
className="fixed inset-0 z-40 bg-black/40"
data-cy="settings-modal-backdrop"
onClick={onClose}
/>

<div className="fixed inset-0 z-50 flex items-center justify-center p-3 sm:p-4">
<div className="w-full max-w-[25rem] rounded-2xl border border-white/35 bg-[#1d1e20] shadow-[0_14px_40px_rgba(0,0,0,0.45)]">
<div
data-cy="settings-modal"
className="w-full max-w-[25rem] rounded-2xl border border-white/35 bg-[#1d1e20] shadow-[0_14px_40px_rgba(0,0,0,0.45)]"
>
{/* Header */}
<div className="flex items-center justify-between border-b border-white/35 px-4 py-2">
<h2 className="font-semibold text-white">Settings</h2>
Expand All @@ -45,7 +52,11 @@ export const SettingsModal: React.FC<Props> = ({ isOpen, onClose }) => {
<div className="px-4 py-3">
<h3 className="mb-2 text-sm text-left font-semibold text-white/70">P-levels</h3>
{P_LEVEL_OPTIONS.map(([lower, upper]) => (
<div key={lower} className="flex items-center gap-3">
<div
key={lower}
data-cy={`p-level-toggle-${lower}-${upper}`}
className="flex items-center gap-3"
>
<div className="-ml-2">
<Toggle
onClick={() => toggle([lower, upper])}
Expand Down
5 changes: 4 additions & 1 deletion apps/nowcasting-app/components/map/color-guide-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ const ColorGuideBar: React.FC<ColorGuideBarProps> = ({ unit }) => {
unitText = "MW";
}
return (
<div className="absolute bg-mapbox-black-700 bottom-12 flex left-0 ml-12 z-20">
<div
data-cy="map-legend"
className="absolute bg-mapbox-black-700 bottom-12 flex left-0 ml-12 z-20"
>
<div className="flex justify-between text-xs h-full text-ocf-black-600 font-bold relative items-end md:text-sm dash:text-xl dash:tracking-wide">
{values?.map((value, index) => (
<div
Expand Down
3 changes: 3 additions & 0 deletions apps/nowcasting-app/components/map/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ const Map: FC<IMap> = ({
>
{showCloudLayer && (
<select
data-cy="satellite-channel-select"
value={activeChannel}
onChange={(e) => setActiveChannel(e.target.value as ChannelSelection)}
disabled={!!satelliteError}
Expand Down Expand Up @@ -471,6 +472,7 @@ const Map: FC<IMap> = ({
<div className="flex flex-row items-end gap-2">
<button
type="button"
data-cy="clouds-toggle-button"
onClick={() => {
const turningOff = showCloudLayer;
setShowCloudLayer(!showCloudLayer);
Expand Down Expand Up @@ -509,6 +511,7 @@ const Map: FC<IMap> = ({

<button
type="button"
data-cy="pv-toggle-button"
title="Toggle the yellow PV forecast overlay so clouds are easier to see"
onClick={() => setShowPvLayer((v) => !v)}
className={`relative inline-flex items-center px-3 py-0.5 text-sm dash:text-lg dash:tracking-wide font-extrabold transition-all active:scale-95 ${
Expand Down
1 change: 1 addition & 0 deletions apps/nowcasting-app/components/play-button/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type UiProps = {
const Ui: React.FC<UiProps> = ({ onClick, isPlaying }) => {
return (
<button
data-cy="play-button"
className="flex items-center w-14 h-14 dash:h-full dash:w-[4rem] dash:2xl:w-[5rem] text-lg dash:text-2xl justify-center text-black bg-ocf-yellow hover:bg-ocf-yellow focus:z-10 focus:bg-ocf-yellow "
onClick={() => {
onClick();
Expand Down
29 changes: 28 additions & 1 deletion apps/nowcasting-app/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { defineConfig } from "cypress";
// Populate process.env with values from .env file
const dotenv = require("dotenv");
const { addMatchImageSnapshotPlugin } = require('@simonsmith/cypress-image-snapshot/plugin');

dotenv.config({ path: ".env.local" });
dotenv.config();

export default defineConfig({
screenshotOnRunFailure: false,
env: {
auth0_username: process.env.NEXT_PUBLIC_AUTH0_USERNAME,
auth0_password: process.env.NEXT_PUBLIC_AUTH0_PASSWORD,
Expand All @@ -16,13 +18,38 @@ export default defineConfig({
auth0_client_secret: process.env.AUTH0_CLIENT_SECRET,
baseUrl: process.env.AUTH0_BASE_URL
},
viewportWidth: 1280,
viewportHeight: 720,
defaultCommandTimeout: 10000,
requestTimeout: 15000,
responseTimeout: 15000,
chromeWebSecurity: false,
// ...rest of the Cypress project config
projectId: process.env.CYPRESS_PROJECT_ID,

e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
addMatchImageSnapshotPlugin(on);

on("task", {
async generateAuth0Cookie(session: any) {
const { generateSessionCookie } = require("@auth0/nextjs-auth0/testing");
const secret = process.env.AUTH0_SECRET;
const cookieVal = await generateSessionCookie(session, {
secret: secret
});
return cookieVal;
}
});

on("before:browser:launch", (browser: any, launchOptions) => {
if (browser.name === "chromium" || browser.family === "chromium") {
launchOptions.args = launchOptions.args.filter((arg) => arg !== "--disable-gpu");
launchOptions.args.push("--ignore-gpu-blacklist");
return launchOptions;
}
return launchOptions;
});
}
}
});
Loading
Loading