add cookie consent banner and tests (#163)

* add cookie consent banner and tests

* update docs and app_diff

* Create .env.client.diff

* Update main.wasp.diff

* add migrattion_lock diff again

* small typo fixes
This commit is contained in:
vincanger 2024-06-06 16:27:37 +02:00 committed by GitHub
parent d762d40f30
commit 602aad0f75
21 changed files with 511 additions and 38 deletions

View File

@ -107,6 +107,8 @@ jobs:
PRO_SUBSCRIPTION_PRICE_ID: ${{ secrets.PRO_SUBSCRIPTION_PRICE_ID }}
CREDITS_PRICE_ID: ${{ secrets.CREDITS_PRICE_ID }}
SKIP_EMAIL_VERIFICATION_IN_DEV: true
# Client-side env vars
REACT_APP_GOOGLE_ANALYTICS_ID: G-H3LSJCK95H
working-directory: ./template
run: |
cd e2e-tests

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
# There is no need to pollute the template with the migrations.
template/app/migrations/
.DS_Store

View File

@ -1,6 +1,6 @@
--- template/app/main.wasp
+++ opensaas-sh/app/main.wasp
@@ -3,24 +3,27 @@
@@ -3,24 +3,24 @@
version: "^0.13.2"
},
@ -25,21 +25,18 @@
"<meta name='twitter:image:width' content='800' />",
"<meta name='twitter:image:height' content='400' />",
"<meta name='twitter:card' content='summary_large_image' />",
- // TODO: You can put your analytics scripts below (https://docs.opensaas.sh/guides/analytics/):
- // If you are going with Plausible:
- // TODO: You can put your Plausible analytics scripts below (https://docs.opensaas.sh/guides/analytics/):
- // NOTE: Plausible does not use Cookies, so you can simply add the scripts here.
- // Google, on the other hand, does, so you must instead add the script dynamically
- // via the Cookie Consent component after the user clicks the "Accept" cookies button.
- "<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.js'></script>", // for production
- "<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.local.js'></script>", // for development
- // If you are going with Google Analytics:
- "<!-- Google tag (gtag.js) --><script>...</script>" // for both production and development
+
+ "<script defer data-domain='opensaas.sh' src='https://plausible.apps.twoducks.dev/js/script.js'></script>",
+ "<script defer data-domain='opensaas.sh' src='https://plausible.apps.twoducks.dev/js/script.local.js'></script>",
+
+ "<!-- Google tag (gtag.js) --><script async src='https://www.googletagmanager.com/gtag/js?id=G-H3LSJCK95H'></script><script>window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'G-H3LSJCK95H');</script>"
],
// 🔐 Auth out of the box! https://wasp-lang.dev/docs/auth/overview
@@ -32,7 +35,7 @@
@@ -32,7 +32,7 @@
email: {
fromField: {
name: "Open SaaS App",
@ -48,7 +45,7 @@
},
emailVerification: {
clientRoute: EmailVerificationRoute,
@@ -44,16 +47,14 @@
@@ -44,16 +44,14 @@
},
userSignupFields: import { getEmailUserFields } from "@src/server/auth/setUsername.js",
},
@ -73,7 +70,7 @@
},
onAuthFailedRedirectTo: "/login",
onAuthSucceededRedirectTo: "/demo-app",
@@ -76,11 +77,11 @@
@@ -76,11 +74,11 @@
// NOTE: "Dummy" provider is just for local development purposes.
// Make sure to check the server logs for the email confirmation url (it will not be sent to an address)!
// Once you are ready for production, switch to e.g. "SendGrid" or "MailGun" providers. Check out https://docs.opensaas.sh/guides/email-sending/ .
@ -87,10 +84,17 @@
},
},
}
@@ -97,6 +98,9 @@
username String? @unique
@@ -93,10 +91,13 @@
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
- email String? @unique
- username String? @unique
+ email String?
+ username String?
lastActiveTimestamp DateTime @default(now())
isAdmin Boolean @default(false)
- isAdmin Boolean @default(false)
+ isAdmin Boolean @default(true)
+ // isMockUser is an extra property for the demo app ensuring that all users can access
+ // the admin dashboard but won't be able to see the other users' data, only mock user data.
+ isMockUser Boolean @default(false)

View File

@ -0,0 +1,11 @@
--- template/app/migrations/20240605151848_remove_has_paid/migration.sql
+++ opensaas-sh/app/migrations/20240605151848_remove_has_paid/migration.sql
@@ -0,0 +1,8 @@
+/*
+ Warnings:
+
+ - You are about to drop the column `hasPaid` on the `User` table. All the data in the column will be lost.
+
+*/
+-- AlterTable
+ALTER TABLE "User" DROP COLUMN "hasPaid";

View File

@ -0,0 +1,75 @@
--- template/app/package-lock.json
+++ opensaas-sh/app/package-lock.json
@@ -45,6 +45,7 @@
"dependencies": {
"@lucia-auth/adapter-prisma": "^4.0.0",
"@prisma/client": "4.16.2",
+ "@sendgrid/mail": "^7.7.0",
"@stitches/react": "^1.2.8",
"@tanstack/react-query": "^4.29.0",
"@testing-library/jest-dom": "^6.3.0",
@@ -2428,6 +2429,49 @@
"win32"
]
},
+ "node_modules/@sendgrid/client": {
+ "version": "7.7.0",
+ "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-7.7.0.tgz",
+ "integrity": "sha512-SxH+y8jeAQSnDavrTD0uGDXYIIkFylCo+eDofVmZLQ0f862nnqbC3Vd1ej6b7Le7lboyzQF6F7Fodv02rYspuA==",
+ "dependencies": {
+ "@sendgrid/helpers": "^7.7.0",
+ "axios": "^0.26.0"
+ },
+ "engines": {
+ "node": "6.* || 8.* || >=10.*"
+ }
+ },
+ "node_modules/@sendgrid/client/node_modules/axios": {
+ "version": "0.26.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
+ "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+ "dependencies": {
+ "follow-redirects": "^1.14.8"
+ }
+ },
+ "node_modules/@sendgrid/helpers": {
+ "version": "7.7.0",
+ "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-7.7.0.tgz",
+ "integrity": "sha512-3AsAxfN3GDBcXoZ/y1mzAAbKzTtUZ5+ZrHOmWQ279AuaFXUNCh9bPnRpN504bgveTqoW+11IzPg3I0WVgDINpw==",
+ "dependencies": {
+ "deepmerge": "^4.2.2"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/@sendgrid/mail": {
+ "version": "7.7.0",
+ "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-7.7.0.tgz",
+ "integrity": "sha512-5+nApPE9wINBvHSUxwOxkkQqM/IAAaBYoP9hw7WwgDNQPxraruVqHizeTitVtKGiqWCKm2mnjh4XGN3fvFLqaw==",
+ "dependencies": {
+ "@sendgrid/client": "^7.7.0",
+ "@sendgrid/helpers": "^7.7.0"
+ },
+ "engines": {
+ "node": "6.* || 8.* || >=10.*"
+ }
+ },
"node_modules/@sinclair/typebox": {
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
@@ -4675,6 +4719,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/defaults": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",

View File

@ -0,0 +1,26 @@
--- template/app/src/server/auth/setUsername.ts
+++ opensaas-sh/app/src/server/auth/setUsername.ts
@@ -4,7 +4,6 @@
export const getEmailUserFields = defineUserSignupFields({
username: (data: any) => data.email,
- isAdmin: (data: any) => adminEmails.includes(data.email),
email: (data: any) => data.email,
});
@@ -13,7 +12,6 @@
// instead of ["user"] and access args.profile.username instead
email: (data: any) => data.profile.emails[0].email,
username: (data: any) => data.profile.login,
- isAdmin: (data: any) => adminEmails.includes(data.profile.emails[0].email),
});
export function getGitHubAuthConfig() {
@@ -25,7 +23,6 @@
export const getGoogleUserFields = defineUserSignupFields({
email: (data: any) => data.profile.email,
username: (data: any) => data.profile.name,
- isAdmin: (data: any) => adminEmails.includes(data.profile.email),
});
export function getGoogleAuthConfig() {

View File

@ -67,6 +67,7 @@ export default defineConfig({
{ label: 'SEO', link: '/guides/seo/' },
{ label: 'Email Sending', link: '/guides/email-sending/' },
{ label: 'File Uploading', link: '/guides/file-uploading/' },
{ label: 'Cookie Consent', link: '/guides/cookie-consent/' },
{ label: 'Deploying', link: '/guides/deploying/' },
],
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 KiB

View File

@ -7,9 +7,9 @@ banner:
---
This guide will show you how to integrate analytics for your app. You can choose between [Google Analytics](#google-analytics) and [Plausible](#plausible).
Google Analytics is free, but tends to be more cumbersome to use.
Google Analytics is free, but uses cookies, so you'll probably want/need to implement the [Cookie Consent Modal](./cookie-consent.md) when using it.
Plausible is an open-source, privacy-friendly alternative to Google Analytics. It's also easier to use than Google if you use their hosted service, which is a paid feature. But, it is completely free if you want to self-host it, although this comes with some additional setup steps.
Plausible is an open-source, privacy-friendly alternative to Google Analytics. **You DO NOT have to use the cookie consent modal** with Plausible, as it does not use cookies. It's also easier to use than Google if you use their hosted service, but be aware it is a paid feature. It is completely free if you want to self-host it, although this comes with some additional setup steps.
If you're looking to add analytics to your blog, you can follow the [Adding Analytics to your Blog](#adding-analytics-to-your-blog) section at the end of this guide.
@ -40,6 +40,9 @@ app OpenSaaS {
Go back to your Plausible dashboard, click on your username in the top right, and click on the `Settings` tab. Scroll down, find your API key and paste it into your `.env.server` file under the `PLAUSIBLE_API_KEY` variable.
:::note[No Cookies]
Plausible does not use cookies, so you don't need to add it to your [Cookie Consent Modal](./cookie-consent.md), hence the script can be added directly to `app.head` in your `main.wasp` file.
:::
### Self-hosted Plausible
@ -58,18 +61,15 @@ As a completely free, open-source project, we appreciate any help 🙏
After you sign up for [Google analytics](https://analytics.google.com/), go to your `Admin` panel in the bottom of the left sidebar and then create a "Property" for your app.
Once you've completed the steps to create a new Property, some Installation Instructions will pop up. Select `install manually` and copy and paste the Google script tag into the `main.wasp` file's head section.
Once you've completed the steps to create a new Property, some Installation Instructions will pop up. Select `install manually` where you should see a string that looks like this:
```js {7}
app OpenSaaS {
wasp: {
version: "^0.13.0"
},
title: "My SaaS App",
head: [
"<your google analytics script tag here>",
],
//...
```sh "<your-google-analytics-id>"
https://www.googletagmanager.com/gtag/js?id=<your-google-analytics-id>
```
and copy and paste the Google Analytics ID into your `.env.client` file to get it working with the [Cookie Consent Modal](./cookie-consent.md) provided with this template:
```sh title=".env.client"
REACT_APP_GOOGLE_ANALYTICS_ID=<your-google-analytics-id> # e.g. G-1234567890
```
:::tip[noscript]

View File

@ -0,0 +1,159 @@
---
title: Cookie Consent Modal
banner:
content: |
⚠️ Open SaaS is now running on <a href='https://wasp-lang.dev'>Wasp v0.13</a>! If you're running an older version of Open SaaS, please follow the
<a href="https://wasp-lang.dev/docs/migrate-from-0-12-to-0-13">migration instructions here</a> ⚠️
---
<img src="/cookie-consent/cookiebanner.png" alt="cookie banner" width="400px" />
Cookie consent banners are annoying, we know. But they are legally required in many countries, so we have to deal with them.
This guide will help you dynamically add or remove cookies from your app via the Cookie Consent modal that comes with this template.
This is needed for *non-essential cookies* that are not necessary for the basic functionality of your app, such as analytics cookies or marketing cookies.
The Modal can be found at `app/src/client/components/cookie-consent/` and contains two main files:
1. `Banner.tsx` - the component that displays the banner at the bottom of the page.
2. `Config.ts` - the configuration file that contains the cookies/scripts that will be dynamically added.
The `Banner.tsx` component is imported in `app/src/client/App.tsx` and is rendered at the bottom of the page, while all the changes to the banner itself are done within the `Config.ts` file, which we explain below.
## Configuration
We decided to use the `vanilla-cookieconsent` library to handle the cookie consent. We've set it up to give you some basic functionality, using mostly the default settings. For a full list of options, you can check the [official documentation](https://www.npmjs.com/package/vanilla-cookieconsent).
Below, we will guide you through the necessary steps to get the cookie consent modal set up for your app.
### Google Analytics
What's impotant to note for this template is that we are simply using the `onAccept` callbacks to dynamically add or remove our [Google Analytics](./analytics.md/#google-analytics) cookies from the page. In order for it to work correctly with your app, you need to add your [Google Analytics ID](./analytics.md/#google-analytics) to your `.env.client` file.
```sh title=".env.client"
REACT_APP_GOOGLE_ANALYTICS_ID=G-1234567890
```
And that's it! The cookie consent modal will now dynamically add or remove the Google Analytics cookies based on the user's choice.
To check if it's working correctly, you can open the browser's developer tools and check the cookies tab. You should see the following cookies being added or removed based on the user's choice:
```sh
_ga
_ga... # Google Analytics cookies.
cc_cookie # Cookie Consent cookie. The name of this cookie can be changed in the config file.
```
### Plausible Analytics
If you decide to go with [Plausible Analytics](./analytics.md/#plausible), you **DO NOT** need to ask users for their consent to use cookies because Plausible, as a privacy-first analytics provider, [does not use cookies](https://plausible.io/privacy-focused-web-analytics). Instead, It collects website usage data anonymously and in aggregate form only, without any personally identifiable information
**By avoiding cookies, Plausible Analytics avoids the need for cookie consent banners.**
### Your Terms / Privacy Policy
You should also add a link to your terms and privacy policy within `consentModal` section of `config.language`:
```ts title="Config.ts" {10,11}
language: {
default: 'en',
translations: {
en: {
consentModal: {
title: 'We use cookies',
// ...
// TODO: Add your own privacy policy and terms and conditions links below.
footer: `
<a href="<your-url-here>" target="_blank">Privacy Policy</a>
<a href="<your-url-here>" target="_blank">Terms and Conditions</a>
`,
},
},
},
}
```
### Allowing Users to Control Certain Cookies (OPTIONAL)
If you've added more than just Google Analytics cookies to your app, you can allow users to control which cookies they want to accept or reject. For example, if you've added marketing cookies, you can add a button to the modal that allows users to reject them, while accepting analytics cookies.
![fine-grained cookie control](/cookie-consent/preferences.png)
To do that, you can change the `preferencesModal.sections` property in `config.language`. Any section that you add to `preferencesModal.sections` must match a `linkedCategory` in the `config.categories` property. Make sure you also add a `showPreferencesBtn` property to `consentModal` (highlighted below).
Below is an example of what your config might look like if you want to give users the option to control over multiple cookie preferences:
```ts title="Config.ts" {7,9-67}
language: {
default: 'en',
translations: {
en: {
consentModal: {
// ...
showPreferencesBtn: 'Manage Individual preferences', // This button will open the preferences modal below.
},
preferencesModal: {
title: 'Manage cookie preferences',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Reject all',
savePreferencesBtn: 'Accept current selection',
closeIconLabel: 'Close modal',
serviceCounterLabel: 'Service|Services',
sections: [
{
title: 'Your Privacy Choices',
description: `In this panel you can express some preferences related to the processing of your personal information. You may review and change expressed choices at any time by resurfacing this panel via the provided link. To deny your consent to the specific processing activities described below, switch the toggles to off or use the “Reject all” button and confirm you want to save your choices.`,
},
{
title: 'Strictly Necessary',
description:
'These cookies are essential for the proper functioning of the website and cannot be disabled.',
linkedCategory: 'necessary',
},
{
title: 'Performance and Analytics',
description:
'These cookies collect information about how you use our website. All of the data is anonymized and cannot be used to identify you.',
linkedCategory: 'analytics',
cookieTable: {
caption: 'Cookie table',
headers: {
name: 'Cookie',
domain: 'Domain',
desc: 'Description',
},
body: [
{
name: '_ga',
domain: location.hostname,
desc: 'Description 1',
},
{
name: '_gid',
domain: location.hostname,
desc: 'Description 2',
},
],
},
},
{
title: 'YouTube',
description: 'This service is used to display video content on the website.',
linkedCategory: 'youtube',
cookieTable: {
// ...
}
},
{
title: 'More information',
description:
'For any queries in relation to my policy on cookies and your choices, please <a href="#contact-page">contact us</a>',
},
],
},
},
},
}
```
For more information on how to do that, check the [official documentation](https://cookieconsent.orestbida.com/reference/configuration-reference.html#translation-preferencesmodal-sections).

View File

@ -3,4 +3,5 @@
# Find your test url at https://dashboard.stripe.com/test/settings/billing/portal
REACT_APP_STRIPE_CUSTOMER_PORTAL=https://billing.stripe.com/...
REACT_APP_GOOGLE_ANALYTICS_ID=G-...
# See https://docs.opensaas.sh/guides/analytics/#google-analytics
REACT_APP_GOOGLE_ANALYTICS_ID=G-...

View File

@ -15,12 +15,12 @@ app OpenSaaS {
"<meta name='twitter:image:width' content='800' />",
"<meta name='twitter:image:height' content='400' />",
"<meta name='twitter:card' content='summary_large_image' />",
// TODO: You can put your analytics scripts below (https://docs.opensaas.sh/guides/analytics/):
// If you are going with Plausible:
// TODO: You can put your Plausible analytics scripts below (https://docs.opensaas.sh/guides/analytics/):
// NOTE: Plausible does not use Cookies, so you can simply add the scripts here.
// Google, on the other hand, does, so you must instead add the script dynamically
// via the Cookie Consent component after the user clicks the "Accept" cookies button.
"<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.js'></script>", // for production
"<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.local.js'></script>", // for development
// If you are going with Google Analytics:
"<!-- Google tag (gtag.js) --><script>...</script>" // for both production and development
],
// 🔐 Auth out of the box! https://wasp-lang.dev/docs/auth/overview

View File

@ -26,6 +26,7 @@
"react-icons": "4.11.0",
"stripe": "11.15.0",
"tailwind-merge": "^2.2.1",
"vanilla-cookieconsent": "^3.0.1",
"wasp": "file:.wasp/out/sdk/wasp",
"zod": "3.22.4"
},
@ -9878,6 +9879,11 @@
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
},
"node_modules/vanilla-cookieconsent": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/vanilla-cookieconsent/-/vanilla-cookieconsent-3.0.1.tgz",
"integrity": "sha512-gqc4x7O9t1I4xWr7x6/jtQWPr4PZK26SmeA0iyTv1WyoECfAGnu5JEOExmMEP+5Fz66AT9OiCBO3GII4wDQHLw=="
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View File

@ -21,6 +21,7 @@
"react-icons": "4.11.0",
"stripe": "11.15.0",
"tailwind-merge": "^2.2.1",
"vanilla-cookieconsent": "^3.0.1",
"wasp": "file:.wasp/out/sdk/wasp",
"zod": "3.22.4"
},

View File

@ -2,6 +2,7 @@ import { useAuth } from 'wasp/client/auth';
import { updateCurrentUser } from 'wasp/client/operations';
import './Main.css';
import AppNavBar from './components/AppNavBar';
import CookieConsentBanner from './components/cookie-consent/Banner';
import { useMemo, useEffect, ReactNode } from 'react';
import { useLocation } from 'react-router-dom';
@ -53,6 +54,7 @@ export default function App({ children }: { children: ReactNode }) {
</>
)}
</div>
<CookieConsentBanner />
</>
);
}

View File

@ -0,0 +1,19 @@
import { useEffect } from 'react';
import * as CookieConsent from 'vanilla-cookieconsent';
import 'vanilla-cookieconsent/dist/cookieconsent.css';
import getConfig from './Config';
/**
* NOTE: if you do not want to use the cookie consent banner, you should
* run `npm uninstall vanilla-cookieconsent`, and delete this component, its config file,
* as well as its import in src/client/App.tsx .
*/
const CookieConsentBanner = () => {
useEffect(() => {
CookieConsent.run(getConfig());
}, []);
return <div id='cookieconsent'></div>;
};
export default CookieConsentBanner;

View File

@ -0,0 +1,116 @@
import type { CookieConsentConfig } from 'vanilla-cookieconsent';
declare global {
interface Window {
dataLayer: any;
}
}
const getConfig = () => {
// See https://cookieconsent.orestbida.com/reference/configuration-reference.html for configuration options.
const config: CookieConsentConfig = {
// Default configuration for the modal.
root: 'body',
autoShow: true,
disablePageInteraction: false,
hideFromBots: import.meta.env.PROD ? true : false, // Set this to false for dev/headless tests otherwise the modal will not be visible.
mode: 'opt-in',
revision: 0,
// Default configuration for the cookie.
cookie: {
name: 'cc_cookie',
domain: location.hostname,
path: '/',
sameSite: 'Lax',
expiresAfterDays: 365,
},
guiOptions: {
consentModal: {
layout: 'box',
position: 'bottom right',
equalWeightButtons: true,
flipButtons: false,
},
},
categories: {
necessary: {
enabled: true, // this category is enabled by default
readOnly: true, // this category cannot be disabled
},
analytics: {
autoClear: {
cookies: [
{
name: /^_ga/, // regex: match all cookies starting with '_ga'
},
{
name: '_gid', // string: exact cookie name
},
],
},
// https://cookieconsent.orestbida.com/reference/configuration-reference.html#category-services
services: {
ga: {
label: 'Google Analytics',
onAccept: () => {
try {
const GA_ANALYTICS_ID = import.meta.env.REACT_APP_GOOGLE_ANALYTICS_ID;
if (!GA_ANALYTICS_ID.length) {
throw new Error('Google Analytics ID is missing');
}
window.dataLayer = window.dataLayer || [];
function gtag(..._args: unknown[]) {
(window.dataLayer as Array<any>).push(arguments);
}
gtag('js', new Date());
gtag('config', GA_ANALYTICS_ID);
// Adding the script tag dynamically to the DOM.
const script = document.createElement('script');
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_ANALYTICS_ID}`;
script.async = true;
document.body.appendChild(script);
} catch (error) {
console.error(error);
}
},
onReject: () => {},
},
},
},
},
language: {
default: 'en',
translations: {
en: {
consentModal: {
title: 'We use cookies',
description:
'We use cookies primarily for analytics to enhance your experience. By accepting, you agree to our use of these cookies. You can manage your preferences or learn more about our cookie policy.',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Reject all',
// showPreferencesBtn: 'Manage Individual preferences', // (OPTIONAL) Activates the preferences modal
// TODO: Add your own privacy policy and terms and conditions links below.
footer: `
<a href="<your-url-here>" target="_blank">Privacy Policy</a>
<a href="<your-url-here>" target="_blank">Terms and Conditions</a>
`,
},
// The showPreferencesBtn activates this modal to manage individual preferences https://cookieconsent.orestbida.com/reference/configuration-reference.html#translation-preferencesmodal
preferencesModal: {
sections: [],
},
},
},
},
};
return config;
};
export default getConfig;

View File

@ -1,11 +1,11 @@
{
"name": "blog2",
"name": "blog",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "blog2",
"name": "blog",
"version": "0.0.1",
"dependencies": {
"@astrojs/check": "^0.6.0",

View File

@ -1,5 +1,5 @@
{
"name": "docs",
"name": "blog",
"type": "module",
"version": "0.0.1",
"scripts": {

View File

@ -1,6 +1,6 @@
import { test, expect } from '@playwright/test';
const DOCS_URL = 'https://docs.opensaas.sh'
const DOCS_URL = 'https://docs.opensaas.sh';
test.describe('general landing page tests', () => {
test.beforeEach(async ({ page }) => {
@ -17,7 +17,56 @@ test.describe('general landing page tests', () => {
});
test('headings', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Frequently asked questions' })).toBeVisible();
await expect(page.getByRole('heading', { name: 'Some cool words' })).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Frequently asked questions' })
).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Some cool words' })
).toBeVisible();
});
});
test.describe('cookie consent tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('cookie consent banner rejection does not set cc_cookie', async ({
context,
page,
}) => {
await page.$$('button:has-text("Reject all")');
await page.click('button:has-text("Reject all")');
const cookies = await context.cookies();
const consentCookie = cookies.find((c) => c.name === 'cc_cookie');
const cookieObject = JSON.parse(decodeURIComponent(consentCookie.value));
expect(cookieObject.categories.includes('analytics')).toBeFalsy();
});
test('cookie consent banner acceptance sets cc_cookie and _ga cookies', async ({
context,
page,
}) => {
await page.$$('button:has-text("Accept all")');
await page.click('button:has-text("Accept all")');
let cookies = await context.cookies();
const consentCookie = cookies.find((c) => c.name === 'cc_cookie');
const cookieObject = JSON.parse(decodeURIComponent(consentCookie.value));
expect(cookieObject.categories.includes('analytics')).toBeTruthy();
// Wait for Google Analytics cookies to be set after accepting
const MAX_TIME = 15000;
const startTime = Date.now();
while (cookies.length === 1) {
if (Date.now() - startTime > MAX_TIME) {
throw new Error('Timeout: Google Analytics cookies not set.');
}
cookies = await context.cookies();
}
const gaCookiesArr = cookies.filter((c) => c.name.startsWith('_ga'));
expect(gaCookiesArr.length === 2).toBeTruthy();
});
});