Skip to content

Commit

Permalink
Merge pull request #22 from button-inc/21-playwright-auth-setup
Browse files Browse the repository at this point in the history
πŸ“š playwright config for auth tests
  • Loading branch information
shon-button authored Aug 4, 2023
2 parents 6ceb9f8 + 7685f2d commit 4d110a2
Show file tree
Hide file tree
Showing 75 changed files with 1,511 additions and 1,481 deletions.
18 changes: 13 additions & 5 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,50 @@
.env
.env.local
Dockerfile
.git
.git
.gitignore
docker-compose*

docker-compose\*

# files form git-ignore

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

# dependencies

/.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*

# vercel

.vercel

# typescript
*.tsbuildinfo

\*.tsbuildinfo
next-env.d.ts
23 changes: 23 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

API_HOST=
DATABASE=
DATABASE_HOST=
DATABASE_PORT=
DATABASE_PROTOCOL=
DATABASE_SCHEMA_ADMIN=
DATABASE_SCHEMA_CLEAN=
DATABASE_SCHEMA_WORKSPACE=
DATABASE_SCHEMA_VAULT=
DATABASE_USER_ADMIN=
DATABASE_USER_PW_ADMIN=
DATABASE_USER_ANALYST=
DATABASE_USER_PW_ANALYST=
DATABASE_USER_DROPPER=
DATABASE_USER_PW_DROPPER=
DATABASE_USER_MANAGER=
DATABASE_USER_PW_MANAGER=
GOOGLE_BUCKET_NAME=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
NEXTAUTH_URL=
NEXTAUTH_SECRET=
14 changes: 12 additions & 2 deletions .github/workflows/scan-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,15 @@ jobs:
target-url: 'http://localhost:3000'
package-manager: 'pnpm'



call-workflow-gitleaks-scan:
uses: button-inc/gh-actions/.github/workflows/[email protected]
with:
notify-user-list: "@shon-button,@YaokunLin"
secrets:
github-token: ${{ secrets.GITHUB_TOKEN }}
gitleaks-license: ${{ secrets.GITLEAKS_LICENSE}}

call-workflow-owasp-zap-scan:
uses: button-inc/gh-actions/.github/workflows/[email protected]
with:
target-url: "http://localhost:3000"
12 changes: 9 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local
.env.*
# all env files except .env.example
**/.env
**/.env.development
!**/.env.example


# vercel
.vercel
Expand All @@ -43,3 +45,7 @@ next-env.d.ts
# keycloak
keycloak-sa-credential.json
emissions-elt-demo-ecc9c7e27bf4.json

# playwright
/tests/.env
playwright/.auth/user.json
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\\[email protected]\\node_modules\\typescript\\lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
}
78 changes: 73 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,10 +212,10 @@ 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
```

To echo the value of the **`GOOGLE_APPLICATION_CREDENTIALS`** environment variable in a terminal you can use the following command:
Expand Down Expand Up @@ -248,6 +252,72 @@ 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
```

**Note**: `pnpm run test:gcs` will run a script `scripts/tests/test-gcs.sh` that configures GOOGLE_APPLICATION_CREDENTIALS from service-account-key.json information stored as a stringify JSON object in `scripts\tests\.env`, for use of the [service account](https://console.cloud.google.com/iam-admin/serviceaccounts/details/106707473171516793046?project=emissions-elt-demo) permisions for GCS authentication.


## Running App Locally

### run dev server
Expand Down Expand Up @@ -359,5 +429,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

0 comments on commit 4d110a2

Please sign in to comment.