Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 10 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,33 +55,40 @@
path: packages/ui/dist

test:
name: Run tests
name: Run tests (Unit + Playwright)
runs-on: ubuntu-latest
defaults:
run:
working-directory: app
needs: [ui]
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install

- uses: actions/download-artifact@v4
with:
name: ui-build
path: packages/ui/dist

- name: Run test
run: pnpm test
- name: Build app
run: pnpm build

- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps

- name: Run Playwright tests
working-directory: packages/e2e-tests
run: pnpm exec playwright test

translation:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
continue-on-error: true
name: Identify error with translation files
runs-on: ubuntu-latest
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30
Comment on lines +9 to +27

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 4 months ago

In general, the fix is to explicitly declare a permissions block at the workflow or job level, limiting GITHUB_TOKEN to only what this workflow needs. Since this job just checks out code, installs dependencies, runs Playwright tests, and uploads artifacts, it only needs read access to repository contents; no write permissions or access to other scopes (issues, pull-requests, etc.) are required.

The best minimal fix without changing existing functionality is to add a permissions block at the root level of .github/workflows/playwright.yml, just under name: (or above jobs:). This will apply to all jobs in the workflow (currently only test) and set contents: read. No other scopes are necessary for the listed actions. Concretely, edit .github/workflows/playwright.yml to insert:

permissions:
  contents: read

between the existing name: Playwright Tests and the on: block, leaving the rest of the workflow unchanged. No additional imports, methods, or definitions are required because this is purely a configuration change.

Suggested changeset 1
.github/workflows/playwright.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -1,4 +1,6 @@
 name: Playwright Tests
+permissions:
+  contents: read
 on:
   push:
     branches: [ main, master ]
EOF
@@ -1,4 +1,6 @@
name: Playwright Tests
permissions:
contents: read
on:
push:
branches: [ main, master ]
Copilot is powered by AI and may make mistakes. Always verify output.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,10 @@ storybook-static/

# Helm
.helm-charts/

# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/
1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@togglecorp/toggle-request": "^1.0.0-beta.3",
"@turf/bbox": "^6.5.0",
"@turf/buffer": "^6.5.0",
"diff": "^8.0.2",
"exceljs": "^4.3.0",
"file-saver": "^2.0.5",
"html-to-image": "^1.11.11",
Expand Down
186 changes: 186 additions & 0 deletions app/src/App/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,50 @@ const emergencyAdditionalInfo = customWrapRoute({
},
});

type DefaultDrefDetailChild = 'dref-detail';
const drefProcessLayout = customWrapRoute({
parent: rootLayout,
path: 'dref-process',
forwardPath: 'dref-detail' satisfies DefaultDrefDetailChild,
component: {
render: () => import('#views/DrefProcess'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'DREF Process',
visibility: 'anything',
},
});

const drefDetail = customWrapRoute({
parent: drefProcessLayout,
path: 'dref-detail' satisfies DefaultDrefDetailChild,
component: {
render: () => import('#views/DrefDetail'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Response and Imminent DREF',
visibility: 'anything',
},
});

const eapDetail = customWrapRoute({
parent: drefProcessLayout,
path: 'eap-detail',
component: {
render: () => import('#views/EarlyActionProtocols'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Early Action Protocols',
visibility: 'anything',
},
});

type DefaultPreparednessChild = 'global-summary';
const preparednessLayout = customWrapRoute({
parent: rootLayout,
Expand Down Expand Up @@ -715,6 +759,49 @@ const accountMyFormsThreeW = customWrapRoute({
},
});

const accountMyFormsEap = customWrapRoute({
parent: accountMyFormsLayout,
path: 'eap-applications',
component: {
render: () => import('#views/AccountMyFormsEap'),
props: {},
},
context: {
title: 'Account - EAP Applications',
visibility: 'is-authenticated',
permissions: ({ isGuestUser }) => !isGuestUser,
},
});

const fullEapForm = customWrapRoute({
parent: rootLayout,
path: 'eap/:eapId/full',
component: {
render: () => import('#views/EapFullForm'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'EAP Full Forms',
visibility: 'is-authenticated',
permissions: ({ isGuestUser }) => !isGuestUser,
},
});

const simplifiedEapForm = customWrapRoute({
parent: rootLayout,
path: 'eap/:eapId/simplified',
component: {
render: () => import('#views/EapSimplifiedForm'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Simplified EAP Form',
visibility: 'is-authenticated',
},
});

const accountNotifications = customWrapRoute({
parent: accountLayout,
path: 'notifications',
Expand Down Expand Up @@ -1094,6 +1181,93 @@ const fieldReportDetails = customWrapRoute({
},
});

type DefaultEapRegistrationChild = 'new';
const eapRegistrationLayout = customWrapRoute({
parent: rootLayout,
path: 'eap-registration',
forwardPath: 'new' satisfies DefaultEapRegistrationChild,
component: {
render: () => import('#views/EapRegistration'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'EAP Process',
visibility: 'is-authenticated',
},
});

const newEapDevelopmentRegistration = customWrapRoute({
parent: eapRegistrationLayout,
path: 'new' satisfies DefaultEapRegistrationChild,
component: {
render: () => import('#views/EapRegistration'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'New EAP Development Registration',
visibility: 'is-authenticated',
permissions: ({ isGuestUser }) => !isGuestUser,
},
});

const eapDevelopmentRegistrationForm = customWrapRoute({
parent: eapRegistrationLayout,
path: ':eapId/',
component: {
render: () => import('#views/EapRegistration'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'View EAP',
visibility: 'is-authenticated',
},
});

const eapFullExport = customWrapRoute({
parent: rootLayout,
path: 'eap/:eapId/export/full',
component: {
render: () => import('#views/EapFullExport'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'EAP Export',
visibility: 'is-authenticated',
},
});

const eapSimplifiedExport = customWrapRoute({
parent: rootLayout,
path: 'eap/:eapId/export/simplified',
component: {
render: () => import('#views/EapSimplifiedExport'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'EAP Export',
visibility: 'is-authenticated',
},
});

const eapSummaryExport = customWrapRoute({
parent: rootLayout,
path: 'eap/:eapId/export/summary',
component: {
render: () => import('#views/EapSummaryExport'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'EAP Full Summary Export',
visibility: 'is-authenticated',
},
});

type DefaultPerProcessChild = 'new';
const perProcessLayout = customWrapRoute({
parent: rootLayout,
Expand Down Expand Up @@ -1317,6 +1491,7 @@ const wrappedRoutes = {
accountMyFormsPer,
accountMyFormsDref,
accountMyFormsThreeW,
accountMyFormsEap,
resources,
search,
allThreeWProject,
Expand Down Expand Up @@ -1353,6 +1528,9 @@ const wrappedRoutes = {
termsAndConditions,
operationalLearning,
montandonLandingPage,
newEapDevelopmentRegistration,
fullEapForm,
simplifiedEapForm,
...regionRoutes,
...countryRoutes,
...surgeRoutes,
Expand All @@ -1363,6 +1541,14 @@ const wrappedRoutes = {
// Redirects
preparednessOperationalLearning,
obsoleteFieldReportDetails,
drefDetail,
eapDetail,
drefProcessLayout,
eapRegistrationLayout,
eapDevelopmentRegistrationForm,
eapFullExport,
eapSimplifiedExport,
eapSummaryExport,
};

export const unwrappedRoutes = unwrapRoute(Object.values(wrappedRoutes));
Expand Down
8 changes: 7 additions & 1 deletion app/src/components/GoMapContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ interface Props {
presentationModeAdditionalAfterContent?: React.ReactNode;
onPresentationModeChange?: (newPresentationMode: boolean) => void;
children?: React.ReactNode;
withFullHeight?: boolean;
}

function GoMapContainer(props: Props) {
Expand All @@ -63,6 +64,7 @@ function GoMapContainer(props: Props) {
presentationModeAdditionalAfterContent,
onPresentationModeChange,
children,
withFullHeight,
} = props;

const strings = useTranslation(i18n);
Expand Down Expand Up @@ -161,6 +163,7 @@ function GoMapContainer(props: Props) {
styles.goMapContainer,
printMode && styles.printMode,
presentationMode && styles.presentationMode,
withFullHeight && styles.withFullHeight,
className,
)}
headingLevel={2}
Expand Down Expand Up @@ -236,12 +239,15 @@ function GoMapContainer(props: Props) {
withPadding={presentationMode}
>
<ListView
className={styles.viewContainer}
layout="block"
spacing={presentationMode ? 'lg' : 'none'}
>
{presentationMode && presentationModeAdditionalBeforeContent}
<div className={styles.relativeWrapper}>
<MapContainer className={styles.map} />
<MapContainer
className={styles.map}
/>
<InfoPopup
infoLabel={strings.infoLabel}
className={styles.mapDisclaimer}
Expand Down
17 changes: 17 additions & 0 deletions app/src/components/GoMapContainer/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@
}
}

&.with-full-height {
flex-grow: 1;
height: 100%;

.view-container {
height: 100%;

.relative-wrapper {
height: 100%;

.map {
height: 100%;
}
}
}
}

&.presentation-mode {
background-color: var(--go-ui-color-background);
width: 100vw;
Expand Down
1 change: 1 addition & 0 deletions app/src/components/Navbar/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"userMenuDrefProcessDescription":"Disaster Response Emergency Fund (DREF) is the quickest way of getting funding directly to local humanitarian actors. Use one of the links below to submit a DREF Application or an update.",
"userMenuCreateDrefApplication":"Create DREF Application",
"myDrefApplications": "My DREF Applications",
"earlyActionProtocols": "Early Action Protocols (EAP)",
"userMenuSurge":"The section displays the summary of deployments within current and ongoing emergencies. Login to see available details",
"userMenuSurgeGlobalOverview":"Surge Global Overview",
"userMenuOperationalToolbox":"Operational Toolbox",
Expand Down
10 changes: 9 additions & 1 deletion app/src/components/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -377,12 +377,20 @@ function Navbar(props: Props) {
</Description>
<DropdownMenuItem
type="link"
to="accountMyFormsDref"
to="drefDetail"
styleVariant="action"
withoutFullWidth
>
{strings.myDrefApplications}
</DropdownMenuItem>
<DropdownMenuItem
type="link"
to="eapDetail"
styleVariant="action"
withoutFullWidth
>
{strings.earlyActionProtocols}
</DropdownMenuItem>
<DropdownMenuItem
type="link"
to="newDrefApplicationForm"
Expand Down
Loading
Loading