Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions .github/workflows/pest_coverage_pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ jobs:
run: php -v

- name: Execute Pest Coverage
run: XDEBUG_MODE=coverage ./vendor/bin/pest --coverage --min=${{ env.PEST_MIN_COVERAGE }}
run: XDEBUG_MODE=coverage ./vendor/bin/pest --exclude-testsuite=Browser --coverage --min=${{ env.PEST_MIN_COVERAGE }}

- name: Execute Pest Type Coverage
run: XDEBUG_MODE=coverage ./vendor/bin/pest --type-coverage --min=${{ env.PEST_MIN_TYPE_COVERAGE }}
run: XDEBUG_MODE=coverage ./vendor/bin/pest --exclude-testsuite=Browser --type-coverage --min=${{ env.PEST_MIN_TYPE_COVERAGE }}

- name: Store Log Artifacts
if: failure()
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pest_database_matrix_pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ jobs:
run: composer install --no-interaction --no-progress --prefer-dist

- name: Pest
run: ./vendor/bin/pest
run: ./vendor/bin/pest --exclude-testsuite=Browser
2 changes: 1 addition & 1 deletion .github/workflows/pest_pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
run: npm run build

- name: Execute Pest Test
run: ./vendor/bin/pest
run: ./vendor/bin/pest --exclude-testsuite=Browser

- name: Store Log Artifacts
if: failure()
Expand Down
119 changes: 119 additions & 0 deletions .github/workflows/pest_pull_request_browser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: Pull Request | CI Pest Browser

on: pull_request

# A new push cancels the run it replaces.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
ci_pest_browser:
# Public repository: GitHub-hosted runners only, never the self-hosted group.
runs-on: ubuntu-latest
name: pull-request | ci pest browser

services:
postgres:
image: postgres:17
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: laravel_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432

env:
PHP_VERSION: 8.5
NODE_VERSION: 26

steps:
# CI always tests against PostgreSQL. These variables beat .env and the
# phpunit.xml <env> defaults (in-memory SQLite). The service gets a
# Docker-assigned host port.
- name: Point the app at the Postgres service
run: |
{
echo "DB_CONNECTION=pgsql"
echo "DB_HOST=127.0.0.1"
echo "DB_PORT=${{ job.services.postgres.ports['5432'] }}"
echo "DB_DATABASE=laravel_test"
echo "DB_USERNAME=postgres"
echo "DB_PASSWORD=postgres"
echo "DB_URL="
} >> "$GITHUB_ENV"

- name: Checkout code
uses: actions/checkout@v7

- name: Setup PHP Environment
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: dom, curl, fileinfo, pgsql, pdo_pgsql, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick

- name: Prepare the .env file
run: cp .env.example .env

- name: Cache Composer Dependencies
uses: actions/cache@v6
with:
path: |
~/.composer/cache/files
~/.composer/cache/repo
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: |
${{ runner.os }}-composer-

- name: Install Composer Dependencies
run: composer install --no-progress --prefer-dist --no-interaction --optimize-autoloader

- name: Publish Laravel assets
run: php artisan vendor:publish --tag=laravel-assets --ansi --force

- name: Setup PostgreSQL Database
run: |
until pg_isready -h localhost -p "$DB_PORT" -U postgres; do sleep 1; done

- name: Generate App Key & Run Migrations
run: |
php artisan key:generate
php artisan migrate:fresh

- name: Setup Node.js Environment
uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- name: Install NPM Dependencies
run: npm ci

- name: Build Frontend Assets
run: npm run build

- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium

- name: Execute Pest Browser Tests
run: ./vendor/bin/pest tests/Browser

- name: Store Log Artifacts
if: failure()
uses: actions/upload-artifact@v7
with:
name: Store report artifacts
path: ./storage/logs

- name: Store Screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: Browser Screenshots
path: ./tests/Browser/Screenshots
if-no-files-found: ignore
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ Thumbs.db
/e2e/playwright-report
/public/widget
/resources/widget

# Pest browser test failure screenshots
/tests/Browser/Screenshots/
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ All notable changes to the API and the widget are documented here. The format fo
- The widget is no longer published to npm: the API serves it at `/widget/v1/feedback.js` (one script tag, no npm, no CDN, updated with every deploy). `npm run build` compiles it into `resources/widget/v1`. The npm package `@codebar-ag/feedback-widget` 1.0.0 stays available but gets no updates. Removed: release workflow, type definitions, SRI file, publint and attw.
- The widget only appears on pages the API accepts: it checks the project config first and stays hidden on a `403` (origin or IP not allowed). Refused `GET` requests now carry `Access-Control-Allow-Origin` so the widget can read the `403`; the preflight for sending stays refused.
- Dev dependencies: vite 8.3.5, typescript-eslint 8.72.0.
- Pest browser tests (`tests/Browser`, Playwright Chromium) for the feedback page: tabs, message card, copy, completing and reopening, refused links. New dev dependencies: pestphp/pest-plugin-browser 5.1, playwright 1.64.1. `composer test` leaves them out, `composer test:browser` runs them.

### Fixed

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ npm run build # builds the widget and copies it to resources
npm --prefix widget run check # lint, types, Vitest, build, size budget
npm --prefix widget run e2e # Playwright: Chromium, Firefox, WebKit, mobile
npm --prefix widget run e2e:full # API and widget together
composer test:browser # Pest browser tests of the feedback page (npx playwright install chromium first)
npm --prefix widget run dev # playground with a mock API on http://localhost:4173/fixtures/index.html
```

Expand Down
42 changes: 31 additions & 11 deletions app/Checks/DatabaseConnectionCountCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,52 @@

namespace App\Checks;

use Illuminate\Database\ConnectionResolverInterface;
use Illuminate\Support\Str;
use Spatie\Health\Checks\Checks\DatabaseConnectionCountCheck as SpatieDatabaseConnectionCountCheck;
use Spatie\Health\Checks\Result;

/**
* spatie's DatabaseConnectionCountCheck with the agreed thresholds (warn
* above 50, fail above 80), reporting ok for apps without a database and
* for SQLite, which has no connection count.
* Informational only: reports the number of open connections of the app's
* own database and never warns or fails. On PostgreSQL only connections to
* the current database count, because Neon servers are shared between apps.
* Apps without a database and SQLite report ok.
*/
class DatabaseConnectionCountCheck extends SpatieDatabaseConnectionCountCheck
{
protected ?int $warningThreshold = 50;

protected int $errorThreshold = 80;

public function run(): Result
{
if (! DatabaseAvailability::isAvailable()) {
return Result::make()->shortSummary('no database')->ok();
}

$connection = $this->connectionName ?? $this->getDefaultConnectionName();

if (config("database.connections.{$connection}.driver") === 'sqlite') {
if (config("database.connections.{$this->connection()}.driver") === 'sqlite') {
return Result::make()->shortSummary('sqlite')->ok();
}

return parent::run();
$connectionCount = $this->getConnectionCount();

return Result::make()
->ok()
->meta(['connection_count' => $connectionCount])
->shortSummary($connectionCount.' '.Str::plural('connection', $connectionCount));
}

protected function getConnectionCount(): int
{
$connection = app(ConnectionResolverInterface::class)->connection($this->connection());

if ($connection->getDriverName() !== 'pgsql') {
return parent::getConnectionCount();
}

$count = $connection->scalar('select count(*) from pg_stat_activity where datname = current_database()');

return is_numeric($count) ? (int) $count : 0;
}

private function connection(): string
{
return $this->connectionName ?? $this->getDefaultConnectionName();
}
}
5 changes: 1 addition & 4 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
use Spatie\Health\Checks\Checks\CacheCheck;
use Spatie\Health\Checks\Checks\DebugModeCheck;
use Spatie\Health\Checks\Checks\EnvironmentCheck;
use Spatie\Health\Checks\Checks\OptimizedAppCheck;
use Spatie\Health\Checks\Checks\ScheduleCheck;
use Spatie\Health\Facades\Health;
use Spatie\SecurityAdvisoriesHealthCheck\SecurityAdvisoriesCheck;
Expand All @@ -41,8 +40,7 @@ public function boot(): void

/**
* Checks for Oh Dear (OH_DEAR_HEALTH_CHECK_*) and `php artisan health:check`:
* the standard set agreed for all Laravel Cloud apps. OptimizedAppCheck
* needs `php artisan optimize` in the deploy command.
* the standard set agreed for all Laravel Cloud apps.
*/
private function configureHealthChecks(): void
{
Expand All @@ -52,7 +50,6 @@ private function configureHealthChecks(): void
DatabaseCheck::new(),
DatabaseConnectionCountCheck::new(),
CacheCheck::new(),
OptimizedAppCheck::new(),
ScheduleCheck::new()->heartbeatMaxAgeInMinutes(120),
SecurityAdvisoriesCheck::new(),
NightwatchIssuesCheck::new()->if(filled(config('default.nightwatch.api_token')) && filled(config('default.nightwatch.application_id'))),
Expand Down
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.9",
"pestphp/pest": "^5.0",
"pestphp/pest-plugin-browser": "^5.1",
"pestphp/pest-plugin-laravel": "^5.0",
"pestphp/pest-plugin-type-coverage": "^5.0"
},
Expand Down Expand Up @@ -55,8 +56,9 @@
"lint": "./vendor/bin/pint --test",
"format": "./vendor/bin/pint",
"analyse": "./vendor/bin/phpstan analyse --memory-limit=1G --no-progress",
"test": "./vendor/bin/pest --parallel --compact",
"test:coverage": "./vendor/bin/pest --coverage --min=90",
"test": "./vendor/bin/pest --exclude-testsuite=Browser --parallel --compact",
"test:coverage": "./vendor/bin/pest --exclude-testsuite=Browser --coverage --min=90",
"test:browser": "./vendor/bin/pest tests/Browser",
"check": [
"@lint",
"@analyse",
Expand Down
Loading
Loading