Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ jobs:

- name: Tests
run: composer test
env:
EMBED_STRICT_CACHE: 1

phpstan:
name: PHPStan Static Analysis
Expand Down
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,74 @@ Note: The built-in detectors does not require settings. This feature is only for

---

## Testing

### Running tests

```bash
composer test
# or
./vendor/bin/phpunit
```

### Snapshot modes

The test suite uses cached HTTP responses and fixtures to avoid network requests during testing. You can control this behavior using environment variables:

| Environment Variable | Description |
|---------------------|-------------|
| `UPDATE_EMBED_SNAPSHOTS=1` | Fetch from network and update both cache and fixtures |
| `EMBED_STRICT_CACHE=1` | Fail if cache or fixture doesn't exist (useful for CI) |

By default (no environment variables set), tests read from cache and generate missing files automatically.

**Note:** If both `UPDATE_EMBED_SNAPSHOTS` and `EMBED_STRICT_CACHE` are set, `UPDATE_EMBED_SNAPSHOTS` takes precedence.

### Cache structure

The test framework uses two types of cached data:

- **Response cache** (`tests/cache/`): Cached HTTP responses from external sites
- **Fixtures** (`tests/fixtures/`): Expected test results (metadata extracted from cached responses)

### How to update cache

#### When a website changes its HTML structure

If a website updates its HTML and you need to update the cached response and fixture:

```bash
# Update cache and fixture for a specific test
UPDATE_EMBED_SNAPSHOTS=1 ./vendor/bin/phpunit --filter testYoutube
```

#### When adding a new test

After adding a new URL to test:

```bash
# This will fetch the response and create both cache and fixture
UPDATE_EMBED_SNAPSHOTS=1 ./vendor/bin/phpunit --filter testNewSite
```

#### Update all caches at once

To refresh all cached responses and fixtures from the network:

```bash
UPDATE_EMBED_SNAPSHOTS=1 ./vendor/bin/phpunit
```

#### For CI environments

Ensure all tests run strictly from cache (fail if any cache is missing):

```bash
EMBED_STRICT_CACHE=1 ./vendor/bin/phpunit
```

---

[ico-version]: https://poser.pugx.org/embed/embed/v/stable
[ico-license]: https://poser.pugx.org/embed/embed/license
[ico-downloads]: https://poser.pugx.org/embed/embed/downloads
Expand Down
74 changes: 72 additions & 2 deletions tests/PagesTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,27 @@

abstract class PagesTestCase extends TestCase
{
/**
* Cache mode
* -1 = Read from cache (throws an exception if the cache doesn't exist)
* 0 = Read from cache (generate the files the first time)
* 1 = Read from net without overriding the cache files
* 2 = Read from net and override the cache files
*
* Can be overridden by environment variables:
* - UPDATE_EMBED_SNAPSHOTS=1 : Fetch from network and update both cache and fixtures
* - EMBED_STRICT_CACHE=1 : Fail if cache or fixture doesn't exist (mode -1)
*/
const CACHE = 0;

/**
* Fixtures mode
* 0 = Do not override the fixtures
* 1 = Override the fixtures
*
* Can be overridden by environment variable:
* - UPDATE_EMBED_SNAPSHOTS=1 : Update both cache and fixtures
*/
const FIXTURES = 0;

private const DETECTORS = [
Expand All @@ -42,14 +62,53 @@ abstract class PagesTestCase extends TestCase

private static Embed $embed;

/**
* Get cache mode from environment variables or class constant
*/
protected static function getCacheMode(): int
{
// UPDATE_EMBED_SNAPSHOTS=1 : Fetch from network and override cache
if (getenv('UPDATE_EMBED_SNAPSHOTS')) {
return 2;
}

// EMBED_STRICT_CACHE=1 : Fail if cache doesn't exist
if (getenv('EMBED_STRICT_CACHE')) {
return -1;
}

return static::CACHE;
}

/**
* Get fixtures mode from environment variable or class constant
*/
protected static function getFixturesMode(): int
{
// UPDATE_EMBED_SNAPSHOTS=1 : Override fixtures with new results
if (getenv('UPDATE_EMBED_SNAPSHOTS')) {
return 1;
}

return static::FIXTURES;
}

/**
* Check if strict cache mode is enabled
*/
protected static function isStrictMode(): bool
{
return (bool) getenv('EMBED_STRICT_CACHE');
}

private static function getEmbed(): Embed
{
if (isset(self::$embed)) {
return self::$embed;
}

$dispatcher = new FileClient(__DIR__.'/cache');
$dispatcher->setMode(static::CACHE);
$dispatcher->setMode(self::getCacheMode());

return self::$embed = new Embed(new Crawler($dispatcher), self::getExtractorFactory());
}
Expand All @@ -63,7 +122,18 @@ protected function assertEmbed(string $url)
$data = self::getData($extractor);
$expected = self::readData($uri);

if (!$expected || static::FIXTURES === 1) {
// In strict mode, fail if fixture is missing
if (!$expected && self::isStrictMode()) {
$this->fail("Fixture not found for {$url}");
}

// Update mode: save fixture and skip
if (self::getFixturesMode() === 1) {
self::writeData($uri, $data);
echo PHP_EOL."Save fixture: {$url}";
$this->markTestSkipped('Skipped assertion for '.$url);
} elseif (!$expected) {
// Missing fixture in non-strict mode: create and skip
self::writeData($uri, $data);
echo PHP_EOL."Save fixture: {$url}";
$this->markTestSkipped('Skipped assertion for '.$url);
Expand Down