Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸ“š playwright config for auth tests #22

Merged
merged 15 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
41 changes: 28 additions & 13 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,43 +1,58 @@
/node_modules
.env
.env.local
Dockerfile
.git
.gitignore
docker-compose*


# files form git-ignore
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies

/node_modules
/.pnp
.pnp.js

# testing

/coverage

# next.js

/.next/
/out/

# production

/build
/dist

# misc

.DS_Store
*.pem
\*.pem

# debug

npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files

.env*.local
.env.*

# vercel

.vercel

# typescript
*.tsbuildinfo

\*.tsbuildinfo
next-env.d.ts
/test-results/
/playwright-report/
/playwright/.cache/

# keycloak

keycloak-sa-credential.json

# gcp

/credentials/service-account-key.json
\nplaywright/.auth
28 changes: 28 additions & 0 deletions .env
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to fix the .gitignore to hide this file?

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
API_HOST=http://localhost:3000/
DATABASE=eed
DATABASE_HOST=34.125.92.26
DATABASE_PORT=5432
DATABASE_PROTOCOL=postgres
DATABASE_SCHEMA_ADMIN=eed
DATABASE_SCHEMA_CLEAN=data_clean_room
DATABASE_SCHEMA_WORKSPACE=data_science_workspace
DATABASE_SCHEMA_VAULT=published_vault
DATABASE_USER_ADMIN=eed_internal
DATABASE_USER_PW_ADMIN=secret
DATABASE_USER_ANALYST=eed_internal
DATABASE_USER_PW_ANALYST=secret
DATABASE_USER_DROPPER=eed_internal
DATABASE_USER_PW_DROPPER=secret
DATABASE_USER_MANAGER=eed_internal
DATABASE_USER_PW_MANAGER=secret
DATABASE_INSTANCE_CONNECTION_NAME=emissions-elt-demo:us-west4:eed
DB_USERNAME=keycloak_actor
DB_PASSWORD=keycloak_pass
GITHUB_ID=291382f53280c6de8f4d
GITHUB_SECRET=aaa22549c1066fbf1138505fa03a28e03ab045b8
GOOGLE_BUCKET_NAME=eed_upload_file_storage
GOOGLE_CLIENT_ID=77682378143-061racvi899q8vvgmcioqhbnu8pokm9o.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=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
GOOGLE_PROJECT_NAME="emissions-elt-demo"
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=ozloLCYWDJNdMl5nh91QNdSj3qMWwpRk
9 changes: 3 additions & 6 deletions .github/workflows/scan-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ concurrency:
jobs:
call-workflow-trivy-scan:
uses: button-inc/gh-actions/.github/workflows/scan-code-trivy.yml@develop

call-workflow-husky-scan:
uses: button-inc/gh-actions/.github/workflows/scan-code-husky.yml@develop
with:
working-directory: ./app
node-version: '18'
node-version: '18'

call-workflow-gitleaks-scan:
uses: button-inc/gh-actions/.github/workflows/scan-code-gitleaks.yml@develop
Expand All @@ -23,11 +23,8 @@ jobs:
secrets:
github-token: ${{ secrets.GITHUB_TOKEN }}
gitleaks-license: ${{ secrets.GITLEAKS_LICENSE}}

call-workflow-owasp-zap-scan:
uses: button-inc/gh-actions/.github/workflows/scan-code-owasp-zap.yml@develop
with:
target-url: 'http://localhost:3000'



2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ repos:
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ["@commitlint/config-conventional"]
additional_dependencies: ["@commitlint/config-conventional"]
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"typescript.tsdk": "node_modules\\.pnpm\\typescript@4.9.4\\node_modules\\typescript\\lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
}
77 changes: 72 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ See [NextAuth.js repo](https://github.com/nextauthjs/next-auth) to learn more.

Within ClimateTrax, the next-auth functionality lies within folder `app\api\[...nextauth]\route.ts` and is managed within `middleware.ts` and `middlewares\withAuthorization.ts`

## `GOOGLE_APPLICATION_CREDENTIALS`
## postgraphile

WIP

## Authenticating with GCP

The `GOOGLE_APPLICATION_CREDENTIALS` environment variable is used by various Google Cloud client libraries and command-line tools to authenticate and authorize access to Google Cloud services. It specifies the path to the service account key file, also known as the Application Default Credentials (ADC) file.

Expand All @@ -208,12 +212,14 @@ To set the **`GOOGLE_APPLICATION_CREDENTIALS`** environment variable within Visu

1. Set the **`GOOGLE_APPLICATION_CREDENTIALS`** environment variable in a terminal you can use the following command:

Linux/Mac:
Linux/Mac: export GOOGLE_APPLICATION_CREDENTIALS=/path/to/keyfile.json

```
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/keyfile.json
export GOOGLE_APPLICATION_CREDENTIALS=credentials/service-account-key-gcs.json
```

**Note**: see package.json\scripts\dev for an example of setting the GAC before starting the dev server. You will need to set .env.development\GAC_EXPORT value to the value of the service account key location. Example: GAC_EXPORT="export GOOGLE_APPLICATION_CREDENTIALS=/credentials/service-account-key-gcs.json"

To echo the value of the **`GOOGLE_APPLICATION_CREDENTIALS`** environment variable in a terminal you can use the following command:

Linux/Mac:
Expand Down Expand Up @@ -248,6 +254,69 @@ On Linux or macOS:
5. Save the file.
6. Restart your terminal or run the `source` command to reload the environment variables in your current session.

## Testing

### Playwright

[Playwright](https://playwright.dev/) is a powerful browser automation library that allows you to control web browsers programmatically.
Playwright comes with the ability to generate tests out of the box and is a great way to quickly get started with testing.

**Creating tests**

You can run codegen and perform actions in the browser recorded as test scripts. Codegen will open two windows, a browser window where you interact with the website you wish to test and the Playwright Inspector window where you can record your tests, copy the tests, clear your tests as well as change the language of your tests. Playwright will generate the code for the user interactions. Codegen will look at the rendered page and figure out the recommended locator, prioritizing role, text and test id locators. If the generator identifies multiple elements matching the locator, it will improve the locator to make it resilient and uniquely identify the target element, therefore eliminating and reducing test(s) failing and flaking due to locators.

Use the codegen command to run the test generator followed by the URL of the website you want to generate tests for. The URL is optional and you can always run the command without it and then add the URL directly into the browser window instead.

```
npx playwright codegen http://localhost:3000/
```

You can also write tests manually following these suggested best practices:

1. Use the right browser context: Playwright provides three browser options: `chromium`, `firefox`, and `webkit`. Choose the browser that best suits your needs in terms of features, performance, and compatibility.

2. Close browser instances: Always close the browser instances and associated resources using the `close()` method. Failing to close the browser can lead to memory leaks and unexpected behavior.

3. Reuse browser contexts: Reusing browser contexts can improve performance. Instead of creating a new context for each new page, consider creating a shared context and reusing it across multiple pages.

4. Use the `waitFor` methods: Playwright offers `waitFor` methods (e.g., `waitForSelector`, `waitForNavigation`) that allow you to wait for specific conditions before proceeding with further actions. This helps ensure that the page has fully loaded or the desired element is available before interacting with it.

5. Emulate network conditions: Playwright allows you to emulate various network conditions, such as slow connections or offline mode, using the `context.route` and `context.routeOverride` methods. This can be helpful for testing how your application behaves under different network scenarios.

6. Handle errors and timeouts: Playwright operations can sometimes fail due to network issues, element unavailability, or other reasons. Properly handle errors and timeouts by using `try-catch` blocks and setting appropriate timeout values for operations like navigation or element waiting.

7. Use `click` and `type` with caution: While using `click` and `type` methods, make sure to target the correct element and account for any potential delays caused by JavaScript events or animations on the page.

8. Configure viewport and device emulation: Playwright allows you to set the viewport size and emulate different devices using the `page.setViewportSize` and `page.emulate` methods. Adjusting the viewport and device emulation can help test the responsiveness of your application.

9. Use selective screenshotting: Capture screenshots strategically to minimize resource usage. Avoid taking excessive screenshots or capturing unnecessary parts of the page unless required for debugging or reporting.

10. Run in headless mode: Consider running Playwright in headless mode (`headless: true`) for improved performance and resource utilization, especially in production or non-visual testing scenarios.

11. Follow Playwright documentation: Playwright has comprehensive documentation with detailed guides, examples, and API references. Consult the official Playwright documentation (https://playwright.dev/) for specific use cases, best practices, and updates.

**Running tests**

Running tests can be completed using the package.json\scripts as follows:

Testing end to end:

```
pnpm run test:e2e
```

Testing i18n:

```
pnpm run test:i18n
```

Testing GCS file upload:

```
pnpm run test:gcs
```

## Running App Locally

### run dev server
Expand Down Expand Up @@ -359,5 +428,3 @@ pnpm run k8s
```

The output of `k8s` will be displayed in the terminal to confirm failure or success of setting a Kubernetes secret using shell "scripts\k8s-secrets.sh"; after which, Cloud Code\Run Kubernetes should launch

<img alt="gitleaks badge" src="https://img.shields.io/badge/protected%20by-gitleaks-blue">
6 changes: 5 additions & 1 deletion app/[lng]/analyst/analytic/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import Analytic from "@/components/routes/Analytic";
import dynamic from "next/dynamic";
//πŸ‘‡οΈ will not be rendered on the server, prevents error: Text content did not match. Server
const Analytic = dynamic(() => import("@/app/components/routes/Analytic"), {
ssr: false,
});
export default function Page() {
return (
<>
Expand Down
5 changes: 1 addition & 4 deletions app/[lng]/analyst/anonymized/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ export default function Page({
id: string;
};
}) {
// πŸ‘‡οΈ graphQL query endpoint for this role
const endpoint = "api/analyst/graphql";

return (
<>
{/* @ts-expect-error Server Component */}
<AnonymizedArea id={params.id} endpoint={endpoint}></AnonymizedArea>
<AnonymizedArea id={params.id}></AnonymizedArea>
</>
);
}
4 changes: 1 addition & 3 deletions app/[lng]/analyst/anonymized/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import Anonymized from "@/components/routes/anonymized/Anonymized";

export default function Page() {
// πŸ‘‡οΈ graphQL query endpoint for this role
const endpoint = "api/analyst/graphql";
return (
<>
{/* @ts-expect-error Server Component */}
<Anonymized endpoint={endpoint}></Anonymized>
<Anonymized></Anonymized>
</>
);
}
16 changes: 16 additions & 0 deletions app/[lng]/analyst/imported/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ImportedArea from "@/components/routes/imported/id/Area";

export default function Page({
params,
}: {
params: {
id: string;
};
}) {
return (
<>
{/* @ts-expect-error Server Component */}
<ImportedArea id={params.id}></ImportedArea>
</>
);
}
4 changes: 1 addition & 3 deletions app/[lng]/analyst/imported/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import Imported from "@/components/routes/imported/Imported";

export default function Page() {
// πŸ‘‡οΈ graphQL query endpoint for this role
const endpoint = "api/analyst/graphql";
return (
<>
{/* @ts-expect-error Server Component */}
<Imported endpoint={endpoint}></Imported>
<Imported></Imported>
</>
);
}
63 changes: 2 additions & 61 deletions app/[lng]/auth/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,12 @@
"use client";
import { getProviders, signIn, ClientSafeProvider } from "next-auth/react";
import React, { useEffect, useState } from "react";
import styles from "./styles.module.css";
import { useTranslation } from "@/i18n/client";
import dynamic from "next/dynamic";
//πŸ‘‡οΈ will not be rendered on the server, prevents error: Text content did not match. Server
const Tag = dynamic(() => import("@/components/layout/Tag"), {
const SignIn = dynamic(() => import("@/components/auth/SignIn"), {
ssr: false,
});
export default function Page() {
const { t } = useTranslation("translation");
const [data, setData] = useState<Record<string, ClientSafeProvider> | null>(
null
);

// πŸ‘‡οΈ code running on the client-side should be placed inside a useEffect hook with the appropriate condition to ensure it only runs in the browser
useEffect(() => {
// πŸ‘‡οΈ call to next-auth providers list
const fetchData = async () => {
const providers = await getProviders();
setData(providers);
};

fetchData();
}, []);

// πŸ‘‡οΈ render the providers as login buttons with the correct calback url
let hostUrl;
if (typeof window !== "undefined") {
hostUrl = window.location.origin;
}
// πŸ‘‡οΈ nextauth signin calback url
const callbackUrl =
hostUrl && hostUrl.includes("http://localhost:4503")
? "http://localhost:3000"
: process.env.NEXTAUTH_URL || "http://localhost:3000";

// πŸ‘‡οΈ nextauth provider signin
const handleSignIn = async (providerId: string) => {
await signIn(providerId, {
callbackUrl,
});
};

const content = data
? Object.values(data).map((provider: ClientSafeProvider) => (
<div key={provider.id} className={styles.provider}>
<button
className={styles.button}
onClick={() => handleSignIn(provider.id)}
>
<img
alt={provider.name}
src={`https://authjs.dev/img/providers/${provider.id}.svg`}
/>
<span>
{t("auth.signin")} {provider.name}
</span>
</button>
</div>
))
: null;

return (
<>
<Tag tag={"auth.tag"} crumbs={[]}></Tag>
{content}
<SignIn />
</>
);
}
Loading