Compare commits
5 Commits
PRWLR-7386
...
api-add-mi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
809fd3dcf3 | ||
|
|
eb5dbab86e | ||
|
|
a470b7c9d8 | ||
|
|
eba5f8e621 | ||
|
|
56443518d6 |
82
.github/workflows/ui-end2end.yml
vendored
@@ -1,82 +0,0 @@
|
||||
name: UI - E2E Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- "v5.*"
|
||||
paths:
|
||||
- 'ui/**'
|
||||
|
||||
env:
|
||||
# Temporary secret for CI test runs only – replace with GitHub Secret later
|
||||
AUTH_SECRET: "N/c6mnaS5+SWq81+819OrzQZlmx1Vxtp/orjttJSmw8="
|
||||
API_BASE_URL: "http://localhost:8080/api/v1"
|
||||
SERVICES_TO_START: "api-dev postgres valkey worker-beat worker-dev"
|
||||
DOCKER_COMPOSE_FILE: "docker-compose-dev.yml"
|
||||
|
||||
jobs:
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: './ui/package-lock.json'
|
||||
|
||||
# - name: Cache Playwright Browsers
|
||||
# uses: actions/cache@v4
|
||||
# with:
|
||||
# path: ~/.cache/ms-playwright
|
||||
# key: playwright-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
|
||||
# restore-keys: |
|
||||
# playwright-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
working-directory: ./ui
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
working-directory: ./ui
|
||||
|
||||
- name: Set up Docker Compose
|
||||
uses: docker/setup-compose-action@364cc21a5de5b1ee4a7f5f9d3fa374ce0ccde746 #v1.2.0
|
||||
- name: Start Docker Compose
|
||||
run: docker compose -f ${DOCKER_COMPOSE_FILE} up -d ${SERVICES_TO_START}
|
||||
|
||||
- name: Wait for API to be ready
|
||||
run: |
|
||||
for i in {1..30}; do
|
||||
if curl -s http://localhost:8000/api/v1; then
|
||||
echo "API is up!"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for API..."
|
||||
sleep 5
|
||||
done
|
||||
- name: Run Playwright tests
|
||||
run: npx playwright test
|
||||
working-directory: ./ui
|
||||
|
||||
- name: Upload Playwright report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: playwright-report
|
||||
path: ./ui/playwright-report
|
||||
|
||||
- name: Upload Playwright videos
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-videos
|
||||
path: ./ui/test-results/**/*.webm
|
||||
|
||||
- name: Docker Compose Down
|
||||
if: always()
|
||||
run: docker compose -f ${DOCKER_COMPOSE_FILE} down
|
||||
@@ -13,6 +13,9 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
### Changed
|
||||
- Reworked `GET /compliance-overviews` to return proper requirement metrics [(#7877)](https://github.com/prowler-cloud/prowler/pull/7877)
|
||||
|
||||
### Fixed
|
||||
- Add missing mapping for ISO 27001 compliance for M365 provider [(#8069)](https://github.com/prowler-cloud/prowler/pull/8069)
|
||||
|
||||
---
|
||||
|
||||
## [v1.8.5] (Prowler v5.7.5)
|
||||
|
||||
@@ -31,6 +31,7 @@ from prowler.lib.outputs.compliance.iso27001.iso27001_gcp import GCPISO27001
|
||||
from prowler.lib.outputs.compliance.iso27001.iso27001_kubernetes import (
|
||||
KubernetesISO27001,
|
||||
)
|
||||
from prowler.lib.outputs.compliance.iso27001.iso27001_m365 import M365ISO27001
|
||||
from prowler.lib.outputs.compliance.kisa_ismsp.kisa_ismsp_aws import AWSKISAISMSP
|
||||
from prowler.lib.outputs.compliance.mitre_attack.mitre_attack_aws import AWSMitreAttack
|
||||
from prowler.lib.outputs.compliance.mitre_attack.mitre_attack_azure import (
|
||||
@@ -88,6 +89,7 @@ COMPLIANCE_CLASS_MAP = {
|
||||
(lambda name: name.startswith("iso27001_"), KubernetesISO27001),
|
||||
],
|
||||
"m365": [
|
||||
(lambda name: name.startswith("iso27001_"), M365ISO27001),
|
||||
(lambda name: name.startswith("cis_"), M365CIS),
|
||||
(lambda name: name == "prowler_threatscore_m365", ProwlerThreatScoreM365),
|
||||
],
|
||||
|
||||
@@ -70,9 +70,13 @@ The other three cases does not need additional configuration, `--az-cli-auth` an
|
||||
Prowler for Azure needs two types of permission scopes to be set:
|
||||
|
||||
- **Microsoft Entra ID permissions**: used to retrieve metadata from the identity assumed by Prowler and specific Entra checks (not mandatory to have access to execute the tool). The permissions required by the tool are the following:
|
||||
- `Domain.Read.All`
|
||||
- `Directory.Read.All`
|
||||
- `Policy.Read.All`
|
||||
- `UserAuthenticationMethod.Read.All` (used only for the Entra checks related with multifactor authentication)
|
||||
|
||||
???+ note
|
||||
You can replace `Directory.Read.All` with `Domain.Read.All` that is a more restrictive permission but you won't be able to run the Entra checks related with DirectoryRoles and GetUsers.
|
||||
|
||||
- **Subscription scope permissions**: required to launch the checks against your resources, mandatory to launch the tool. It is required to add the following RBAC builtin roles per subscription to the entity that is going to be assumed by the tool:
|
||||
- `Reader`
|
||||
- `ProwlerRole` (custom role with minimal permissions defined in [prowler-azure-custom-role](https://github.com/prowler-cloud/prowler/blob/master/permissions/prowler-azure-custom-role.json))
|
||||
@@ -205,12 +209,17 @@ Prowler for M365 requires two types of permission scopes to be set (if you want
|
||||
|
||||
- **Service Principal Application Permissions**: These are set at the **application** level and are used to retrieve data from the identity being assessed:
|
||||
- `AuditLog.Read.All`: Required for Entra service.
|
||||
- `Domain.Read.All`: Required for all services.
|
||||
- `Organization.Read.All`: Required for retrieving tenant information.
|
||||
- `Directory.Read.All`: Required for all services.
|
||||
- `Policy.Read.All`: Required for all services.
|
||||
- `SharePointTenantSettings.Read.All`: Required for SharePoint service.
|
||||
- `User.Read` (IMPORTANT: this must be set as **delegated**): Required for the sign-in.
|
||||
|
||||
???+ note
|
||||
You can replace `Directory.Read.All` with `Domain.Read.All` is a more restrictive permission but you won't be able to run the Entra checks related with DirectoryRoles and GetUsers.
|
||||
|
||||
> If you do this you will need to add also the `Organization.Read.All` permission to the service principal application in order to authenticate.
|
||||
|
||||
|
||||
|
||||
- **Powershell Modules Permissions**: These are set at the `M365_USER` level, so the user used to run Prowler must have one of the following roles:
|
||||
- `Global Reader` (recommended): this allows you to read all roles needed.
|
||||
|
||||
@@ -90,11 +90,14 @@ A Service Principal is required to grant Prowler the necessary privileges.
|
||||
|
||||
Assign the following Microsoft Graph permissions:
|
||||
|
||||
- Domain.Read.All
|
||||
- Directory.Read.All
|
||||
|
||||
- Policy.Read.All
|
||||
- Policy.Read.All
|
||||
|
||||
- UserAuthenticationMethod.Read.All (optional, for MFA checks)
|
||||
- UserAuthenticationMethod.Read.All (optional, for MFA checks)
|
||||
|
||||
???+ note
|
||||
You can replace `Directory.Read.All` with `Domain.Read.All` that is a more restrictive permission but you won't be able to run the Entra checks related with DirectoryRoles and GetUsers.
|
||||
|
||||
1. Go to your App Registration > `API permissions`
|
||||
|
||||
@@ -107,7 +110,7 @@ Assign the following Microsoft Graph permissions:
|
||||
|
||||
3. Search and select:
|
||||
|
||||
- `Domain.Read.All`
|
||||
- `Directory.Read.All`
|
||||
- `Policy.Read.All`
|
||||
- `UserAuthenticationMethod.Read.All`
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 62 KiB |
@@ -95,12 +95,18 @@ With this done you will have all the needed keys, summarized in the following ta
|
||||
### Grant required API permissions
|
||||
|
||||
Assign the following Microsoft Graph permissions:
|
||||
|
||||
- `AuditLog.Read.All`: Required for Entra service.
|
||||
- `Domain.Read.All`: Required for all services.
|
||||
- `Directory.Read.All`: Required for all services.
|
||||
- `Policy.Read.All`: Required for all services.
|
||||
- `SharePointTenantSettings.Read.All`: Required for SharePoint service.
|
||||
- `User.Read` (IMPORTANT: this is set as **delegated**): Required for the sign-in.
|
||||
|
||||
???+ note
|
||||
You can replace `Directory.Read.All` with `Domain.Read.All` is a more restrictive permission but you won't be able to run the Entra checks related with DirectoryRoles and GetUsers.
|
||||
|
||||
> If you do this you will need to add also the `Organization.Read.All` permission to the service principal application in order to authenticate.
|
||||
|
||||
Follow these steps to assign the permissions:
|
||||
|
||||
1. Go to your App Registration > Select your Prowler App created before > click on `API permissions`
|
||||
@@ -113,8 +119,7 @@ Follow these steps to assign the permissions:
|
||||
|
||||
3. Search and select every permission below and once all are selected click on `Add permissions`:
|
||||
- `AuditLog.Read.All`: Required for Entra service.
|
||||
- `Domain.Read.All`
|
||||
- `Organization.Read.All`
|
||||
- `Directory.Read.All`
|
||||
- `Policy.Read.All`
|
||||
- `SharePointTenantSettings.Read.All`
|
||||
|
||||
@@ -134,7 +139,7 @@ Follow these steps to assign the permissions:
|
||||
|
||||

|
||||
|
||||
6. Click `Add permissions`, then **grant admin consent**
|
||||
6. After adding all the permissions, click on `Grant admin consent`
|
||||
|
||||

|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 181 KiB |
@@ -37,6 +37,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Azure Databricks service integration for Azure provider, including the `databricks_workspace_vnet_injection_enabled` check [(#8008)](https://github.com/prowler-cloud/prowler/pull/8008)
|
||||
- Azure Databricks check `databricks_workspace_cmk_encryption_enabled` to ensure workspaces use customer-managed keys (CMK) for encryption at rest [(#8017)](https://github.com/prowler-cloud/prowler/pull/8017)
|
||||
- Add `storage_account_default_to_entra_authorization_enabled` check for Azure provider. [(#7981)](https://github.com/prowler-cloud/prowler/pull/7981)
|
||||
- Replace `Domain.Read.All` with `Directory.Read.All` in Azure and M365 docs [(#8075)](https://github.com/prowler-cloud/prowler/pull/8075)
|
||||
|
||||
### Removed
|
||||
- OCSF version number references to point always to the latest [(#8064)](https://github.com/prowler-cloud/prowler/pull/8064)
|
||||
|
||||
@@ -45,38 +45,45 @@ class Entra(AzureService):
|
||||
for tenant, client in self.clients.items():
|
||||
users_list = await client.users.get()
|
||||
users.update({tenant: {}})
|
||||
for user in users_list.value:
|
||||
users[tenant].update(
|
||||
{
|
||||
user.id: User(
|
||||
id=user.id,
|
||||
name=user.display_name,
|
||||
authentication_methods=[
|
||||
AuthMethod(
|
||||
id=auth_method.id,
|
||||
type=getattr(auth_method, "odata_type", None),
|
||||
)
|
||||
for auth_method in (
|
||||
await client.users.by_user_id(
|
||||
user.id
|
||||
).authentication.methods.get()
|
||||
).value
|
||||
],
|
||||
)
|
||||
}
|
||||
)
|
||||
try:
|
||||
for user in users_list.value:
|
||||
users[tenant].update(
|
||||
{
|
||||
user.id: User(
|
||||
id=user.id,
|
||||
name=user.display_name,
|
||||
authentication_methods=[
|
||||
AuthMethod(
|
||||
id=auth_method.id,
|
||||
type=getattr(
|
||||
auth_method, "odata_type", None
|
||||
),
|
||||
)
|
||||
for auth_method in (
|
||||
await client.users.by_user_id(
|
||||
user.id
|
||||
).authentication.methods.get()
|
||||
).value
|
||||
],
|
||||
)
|
||||
}
|
||||
)
|
||||
except Exception as error:
|
||||
if (
|
||||
error.__class__.__name__ == "ODataError"
|
||||
and error.__dict__.get("response_status_code", None) == 403
|
||||
):
|
||||
logger.error(
|
||||
"You need 'UserAuthenticationMethod.Read.All' permission to access this information. It only can be granted through Service Principal authentication."
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
if (
|
||||
error.__class__.__name__ == "ODataError"
|
||||
and error.__dict__.get("response_status_code", None) == 403
|
||||
):
|
||||
logger.error(
|
||||
"You need 'UserAuthenticationMethod.Read.All' permission to access this information. It only can be granted through Service Principal authentication."
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
return users
|
||||
|
||||
|
||||
4
ui/.gitignore
vendored
@@ -34,7 +34,3 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# Playwright test artifacts
|
||||
playwright-report/
|
||||
test-results/
|
||||
|
||||
@@ -21,7 +21,6 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
- Improve `Scan ID` filter by adding more context and enhancing the UI/UX [(#7979)](https://github.com/prowler-cloud/prowler/pull/7979)
|
||||
- Lighthouse chat interface [(#7878)](https://github.com/prowler-cloud/prowler/pull/7878)
|
||||
- Google Tag Manager integration [(#8058)](https://github.com/prowler-cloud/prowler/pull/8058)
|
||||
- Added initial Playwright configuration and sample test [(#8081)](https://github.com/prowler-cloud/prowler/pull/8081)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
|
||||
77
ui/package-lock.json
generated
@@ -62,7 +62,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/react": "^5.2.0",
|
||||
"@playwright/test": "^1.53.1",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/node": "20.5.7",
|
||||
"@types/react": "18.3.3",
|
||||
@@ -71,7 +70,6 @@
|
||||
"@typescript-eslint/eslint-plugin": "^7.10.0",
|
||||
"@typescript-eslint/parser": "^7.10.0",
|
||||
"autoprefixer": "10.4.19",
|
||||
"dotenv": "^16.5.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-next": "^14.2.23",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
@@ -4718,22 +4716,6 @@
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.53.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.1.tgz",
|
||||
"integrity": "sha512-Z4c23LHV0muZ8hfv4jw6HngPJkbbtZxTkxPNIg7cJcTc9C28N/p2q7g3JZS2SiKBBHJ3uM1dgDye66bB7LEk5w==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.53.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/number": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz",
|
||||
@@ -10097,19 +10079,6 @@
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.5.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
@@ -11404,20 +11373,6 @@
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
@@ -14671,38 +14626,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.53.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.1.tgz",
|
||||
"integrity": "sha512-LJ13YLr/ocweuwxyGf1XNFWIU4M2zUSo149Qbp+A4cpwDjsxRPj7k6H25LBrEHiEwxvRbD8HdwvQmRMSvquhYw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.53.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.53.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.1.tgz",
|
||||
"integrity": "sha512-Z46Oq7tLAyT0lGoFx4DOuB1IA9D1TPj0QkYxpPVUnGDqHHvDpCftu1J2hM2PiWsNMoZh8+LQaarAWcDfPBc6zg==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/possible-typed-array-names": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/react": "^5.2.0",
|
||||
"@playwright/test": "^1.53.1",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/node": "20.5.7",
|
||||
"@types/react": "18.3.3",
|
||||
@@ -63,7 +62,6 @@
|
||||
"@typescript-eslint/eslint-plugin": "^7.10.0",
|
||||
"@typescript-eslint/parser": "^7.10.0",
|
||||
"autoprefixer": "10.4.19",
|
||||
"dotenv": "^16.5.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-next": "^14.2.23",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
@@ -98,8 +96,7 @@
|
||||
"lint:fix": "eslint . --ext .ts,.tsx -c .eslintrc.cjs --fix",
|
||||
"format:check": "./node_modules/.bin/prettier --check ./app",
|
||||
"format:write": "./node_modules/.bin/prettier --config .prettierrc.json --write ./app",
|
||||
"prepare": "husky",
|
||||
"test:e2e": "npx playwright test && npx playwright show-report"
|
||||
"prepare": "husky"
|
||||
},
|
||||
"overrides": {
|
||||
"@react-types/shared": "3.26.0"
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
import * as dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
|
||||
const isLocal = process.env.LOCAL === "true";
|
||||
|
||||
export default defineConfig({
|
||||
timeout: 90 * 1000,
|
||||
testDir: "./tests/e2e",
|
||||
fullyParallel: false,
|
||||
forbidOnly: !isLocal,
|
||||
retries: isLocal ? 0 : 2,
|
||||
workers: isLocal ? undefined : 1,
|
||||
reporter: "html",
|
||||
use: {
|
||||
baseURL: "http://localhost:3000",
|
||||
trace: "on-first-retry",
|
||||
screenshot: "only-on-failure",
|
||||
video: "retain-on-failure",
|
||||
navigationTimeout: 60 * 1000,
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
|
||||
// {
|
||||
// name: 'firefox',
|
||||
// use: { ...devices['Desktop Firefox'] },
|
||||
// },
|
||||
|
||||
// {
|
||||
// name: 'webkit',
|
||||
// use: { ...devices['Desktop Safari'] },
|
||||
// },
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: { ...devices['iPhone 12'] },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: isLocal
|
||||
? undefined // Skip web server in local runs
|
||||
: {
|
||||
command: "npm run dev",
|
||||
url: "http://localhost:3000",
|
||||
reuseExistingServer: true,
|
||||
timeout: 120 * 1000, // wait up to 2 minutes for frontend to boot
|
||||
},
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
# Playwright E2E Testing
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
Playwright is already set up. To install dependencies:
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
npm install
|
||||
|
||||
# Run all tests (headless)
|
||||
npm run test:e2e
|
||||
|
||||
# Run specific file (headless)
|
||||
npx playwright test tests/e2e/root.spec.ts
|
||||
|
||||
# Run all tests with UI (headed mode)
|
||||
npx playwright test --headed
|
||||
|
||||
# Run specific file with UI (headed mode)
|
||||
npx playwright test tests/e2e/root.spec.ts --headed
|
||||
|
||||
# Open the HTML report from last test run
|
||||
npx playwright show-report
|
||||
@@ -1,69 +0,0 @@
|
||||
import { test, expect, request, Page } from '@playwright/test';
|
||||
|
||||
// Test credentials
|
||||
const testEmail = 'test@gmail.com';
|
||||
const testPassword = 'Testt@123456';
|
||||
|
||||
// Helper login function
|
||||
const login = async (page: Page, email: string, password: string) => {
|
||||
await page.goto('/sign-in');
|
||||
await page.fill('input[name="email"]', email);
|
||||
await page.fill('input[name="password"]', password);
|
||||
await page.getByRole('button', { name: /log in/i }).click();
|
||||
};
|
||||
|
||||
test.beforeAll(async () => {
|
||||
const apiContext = await request.newContext();
|
||||
const response = await apiContext.post(`${process.env.API_BASE_URL}/users`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
'Accept': 'application/vnd.api+json',
|
||||
},
|
||||
data: {
|
||||
data: {
|
||||
type: 'users',
|
||||
attributes: {
|
||||
name: 'testuser',
|
||||
email: testEmail,
|
||||
password: testPassword,
|
||||
company_name: 'test',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok()) {
|
||||
console.warn(`User creation may have failed: ${response.status()} - ${await response.text()}`);
|
||||
}
|
||||
|
||||
await apiContext.dispose();
|
||||
});
|
||||
|
||||
// Test invalid login
|
||||
test('should show error for invalid credentials', async ({ page }) => {
|
||||
await login(page, 'wrong@gmail.com', 'WrongPassword123');
|
||||
await page.waitForTimeout(7000);
|
||||
await expect(page.getByText(/invalid email or password/i)).toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
|
||||
// Test valid login and redirection
|
||||
test('should sign in successfully', async ({ page }) => {
|
||||
await login(page, testEmail, testPassword);
|
||||
await page.waitForTimeout(7000);
|
||||
await page.waitForURL((url) => !url.pathname.includes('sign-in'), {
|
||||
timeout: 15000,
|
||||
});
|
||||
});
|
||||
|
||||
// Test session persistence after reload
|
||||
test('should persist session after login', async ({ page }) => {
|
||||
await login(page, testEmail, testPassword);
|
||||
await page.waitForTimeout(7000);
|
||||
await page.waitForURL((url) => !url.pathname.includes('sign-in'), { timeout: 15000 });
|
||||
await page.reload();
|
||||
|
||||
await expect(page.getByRole('button', { name: /sign out/i })).toBeVisible();
|
||||
|
||||
await page.goto("/findings")
|
||||
await expect(page.getByText(/Browse all findings/i).first()).toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('Unauthenticated users are redirected to sign-in and can navigate to sign-up', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page).toHaveURL(/\/sign-in/);
|
||||
await expect(page.getByText('Sign In')).toBeVisible();
|
||||
});
|
||||