diff --git a/.all-contributorsrc b/.all-contributorsrc
index 778abd0e19..a3241e045d 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -215,10 +215,20 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "NoHara42",
+ "name": "Ned O'Hara",
+ "avatar_url": "https://avatars.githubusercontent.com/u/43496778?v=4",
+ "profile": "https://github.com/NoHara42",
+ "contributions": [
+ "code"
+ ]
}
],
"projectName": "community-platform",
"projectOwner": "ONEARMY",
"repoType": "github",
- "repoHost": "https://github.com"
+ "repoHost": "https://github.com",
+ "commitConvention": "angular"
}
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 004688b64f..76acb42d06 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -17,6 +17,8 @@ orbs:
browser-tools: circleci/browser-tools@1.1.3
# used to enable slack integration (required api key set in environment)
slack: circleci/slack@4.4.2
+ # used to track coverage
+ codecov: codecov/codecov@3.2.4
######################################################################################################
# Aliases - code snippets that can be included inline in any other markup
@@ -284,6 +286,12 @@ jobs:
- run:
# NOTE - run-in-band to try reduce memory leaks (https://github.com/facebook/jest/issues/7874)
command: yarn run test:unit:ci && yarn run test:components
+ - store_artifacts:
+ path: coverage
+ - store_artifacts:
+ path: packages/components/coverage
+ - codecov/upload
+
test_circular_dependency:
docker: *docker
steps:
@@ -295,6 +303,7 @@ jobs:
command: yarn workspaces focus one-army-community-platform
- run:
command: yarn run test:madge
+
build:
<<: *docker_matrix
environment:
diff --git a/README.md b/README.md
index a11e8dd910..1d54fd19c2 100644
--- a/README.md
+++ b/README.md
@@ -85,6 +85,7 @@ Thanks go to these wonderful people ([emoji key](https://allcontributors.org/doc
 Devtato 💻 |
+  Ned O'Hara 💻 |
diff --git a/functions/src/emailNotifications/createEmail.ts b/functions/src/emailNotifications/createEmail.ts
index 883d1b5cfb..e659178597 100644
--- a/functions/src/emailNotifications/createEmail.ts
+++ b/functions/src/emailNotifications/createEmail.ts
@@ -13,6 +13,7 @@ const getResourceLabelFromNotificationType = (type: NotificationType) => {
return 'research'
case 'howto_useful':
case 'howto_mention':
+ case 'new_comment':
return 'how-to'
}
}
diff --git a/functions/src/emailNotifications/index.ts b/functions/src/emailNotifications/index.ts
index 29b2848196..fe487b4a4f 100644
--- a/functions/src/emailNotifications/index.ts
+++ b/functions/src/emailNotifications/index.ts
@@ -1,8 +1,41 @@
import * as functions from 'firebase-functions'
import { createNotificationEmails } from './createEmail'
+import { db } from '../Firebase/firestoreDB'
+import { DB_ENDPOINTS, IUserDB } from '../models'
/** Trigger daily process to send any pending email notifications */
exports.sendDaily = functions.pubsub
// Trigger daily at 5pm (https://crontab.guru/#0_17_*_*_*)
.schedule('0 17 * * *')
.onRun(async () => createNotificationEmails())
+
+exports.sendOnce = functions.https.onCall(async (_, context) => {
+ if (!context.auth) {
+ throw new functions.https.HttpsError(
+ 'failed-precondition',
+ 'The function must be called while authenticated.',
+ )
+ }
+ // Validate user exists and has admin status before triggering function.
+ const { uid } = context.auth
+ const user = await db.collection(DB_ENDPOINTS.users).doc(uid).get()
+ if (user.exists) {
+ const { userRoles } = user.data() as IUserDB
+ if (userRoles?.some((role) => ['admin', 'super-admin'].includes(role))) {
+ try {
+ await createNotificationEmails()
+ return 'OK'
+ } catch (error) {
+ console.error(error)
+ throw new functions.https.HttpsError(
+ 'internal',
+ 'There was an error creating emails.',
+ )
+ }
+ }
+ }
+ throw new functions.https.HttpsError(
+ 'permission-denied',
+ 'Emails can be triggered by admins only.',
+ )
+})
diff --git a/package.json b/package.json
index 818cbc2bbf..091685cad9 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,7 @@
"test": "yarn workspace oa-cypress start",
"test:components": "yarn workspace oa-components test",
"test:unit": "env-cmd -e cra craco test --env=jsdom",
- "test:unit:ci": "yarn build:themes && yarn build:components && env-cmd -e cra craco test --env=jsdom --runInBand --logHeapUsage",
+ "test:unit:ci": "yarn build:themes && yarn build:components && env-cmd -e cra craco test --env=jsdom --runInBand --logHeapUsage --coverage",
"test:madge": "npx madge --circular --extensions ts,tsx ./ --exclude src/stores",
"storybook": "yarn workspace oa-components start",
"storybook:build": "yarn build:themes && yarn workspace oa-components build:sb",
diff --git a/packages/components/.gitignore b/packages/components/.gitignore
index 7fa505ce04..390cc2fb1c 100644
--- a/packages/components/.gitignore
+++ b/packages/components/.gitignore
@@ -1,3 +1,4 @@
node_modules
storybook-static
-dist
\ No newline at end of file
+dist
+coverage
\ No newline at end of file
diff --git a/packages/components/package.json b/packages/components/package.json
index 4040b5c3fe..5fd002cdc3 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -14,7 +14,7 @@
"dev": "tsc --watch",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx src --color",
"new-component": "ts-node scripts/newComponent.ts",
- "test": "vitest"
+ "test": "vitest --coverage"
},
"dependencies": {
"@emotion/react": "^11.10.6",
@@ -36,7 +36,7 @@
"storybook": "^7.0.4",
"theme-ui": "^0.15.7",
"use-debounce": "^8.0.4",
- "webpack": "^5.75.0",
+ "webpack": "5.76.0",
"yup": "^1.1.1"
},
"peerDependencies": {
@@ -56,6 +56,7 @@
"@types/react-flag-icon-css": "^1.0.5",
"@types/react-portal": "^4.0.4",
"@vitejs/plugin-react": "^3.1.0",
+ "@vitest/coverage-c8": "^0.30.1",
"babel-loader": "8.1.0",
"eslint": "^7.32.0",
"eslint-plugin-import": "^2.25.4",
@@ -67,6 +68,6 @@
"react-dom": "^17.0.2",
"ts-node": "^10.7.0",
"typescript": "^4.5.5",
- "vitest": "^0.29.5"
+ "vitest": "^0.30.1"
}
}
diff --git a/packages/components/src/Button/Button.tsx b/packages/components/src/Button/Button.tsx
index 950d5a0a60..5c15f65ccf 100644
--- a/packages/components/src/Button/Button.tsx
+++ b/packages/components/src/Button/Button.tsx
@@ -11,6 +11,7 @@ export interface IBtnProps extends React.ButtonHTMLAttributes {
small?: boolean
large?: boolean
showIconOnly?: boolean
+ iconColor?: string
}
type ToArray = [Type] extends [any] ? Type[] : never
@@ -112,7 +113,7 @@ export const Button = (props: BtnProps) => {
pointerEvents: 'none',
}}
>
-
+
)}
`
min-width: ${(props) => (props.size ? `${props.size}px` : '32px')};
min-height: ${(props) => (props.size ? `${props.size}px` : '32px')};
position: relative;
- color: ${(props) => (props.color ? `${props.color}` : 'inherit')};
- ${verticalAlign}
- ${space}
-
- ${(props) =>
+ color: ${(props) => props.color || 'inherit'};
+ ${verticalAlign} ${space}
+ ${(props) =>
props.onClick &&
`
cursor: pointer;
- `}
+ `};
`
const sizeMap = {
diff --git a/packages/components/src/UserStatistics/__snapshots__/UserStatistics.test.tsx.snap b/packages/components/src/UserStatistics/__snapshots__/UserStatistics.test.tsx.snap
index 87c4ecb63b..5c2121c4bf 100644
--- a/packages/components/src/UserStatistics/__snapshots__/UserStatistics.test.tsx.snap
+++ b/packages/components/src/UserStatistics/__snapshots__/UserStatistics.test.tsx.snap
@@ -12,7 +12,7 @@ exports[`UserStatistics > renders correctly 1`] = `
class="css-i1ihic-Box"
>
![]()
renders correctly 1`] = `
class="css-i1ihic-Box"
>