mirror of
https://github.com/pestphp/pest.git
synced 2026-09-05 14:23:34 +02:00
Compare commits
5 Commits
v5.1.0
...
a8d4770c0b
| Author | SHA1 | Date | |
|---|---|---|---|
| a8d4770c0b | |||
| 208f447a10 | |||
| 6b7ca13660 | |||
| bf7c6b3134 | |||
| 617226ca01 |
@@ -190,6 +190,24 @@ async function listPageFiles(pagesDir) {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `existsSync()` ignores casing on APFS and NTFS, and on a macOS bind mount seen from
|
||||||
|
// inside a Linux container, so the `resources/js/Pages` candidate resolves in a project
|
||||||
|
// whose directory is really `resources/js/pages`. Every page path derives from the
|
||||||
|
// directory accepted below, so a wrong-cased one detaches the pages tree from the paths
|
||||||
|
// the PHP side reports. `readdir()` is case-exact everywhere.
|
||||||
|
export async function matchesDiskCasing(projectRoot, rel) {
|
||||||
|
let current = projectRoot
|
||||||
|
|
||||||
|
for (const segment of rel.split('/')) {
|
||||||
|
let entries
|
||||||
|
try { entries = await readdir(current) } catch { return false }
|
||||||
|
if (!entries.includes(segment)) return false
|
||||||
|
current = join(current, segment)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
async function discoverPagesDir() {
|
async function discoverPagesDir() {
|
||||||
const override = process.env.TIA_VITE_PAGES_DIR
|
const override = process.env.TIA_VITE_PAGES_DIR
|
||||||
if (override && override.length > 0) {
|
if (override && override.length > 0) {
|
||||||
@@ -199,6 +217,7 @@ async function discoverPagesDir() {
|
|||||||
for (const rel of PAGE_DIR_CANDIDATES) {
|
for (const rel of PAGE_DIR_CANDIDATES) {
|
||||||
const abs = resolve(PROJECT_ROOT, rel)
|
const abs = resolve(PROJECT_ROOT, rel)
|
||||||
if (!existsSync(abs)) continue
|
if (!existsSync(abs)) continue
|
||||||
|
if (!(await matchesDiskCasing(PROJECT_ROOT, rel))) continue
|
||||||
const files = await listPageFiles(abs)
|
const files = await listPageFiles(abs)
|
||||||
if (files.length > 0) return abs
|
if (files.length > 0) return abs
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -23,7 +23,7 @@
|
|||||||
"nunomaduro/termwind": "^2.4.0",
|
"nunomaduro/termwind": "^2.4.0",
|
||||||
"pestphp/pest-plugin": "^5.0.0",
|
"pestphp/pest-plugin": "^5.0.0",
|
||||||
"pestphp/pest-plugin-arch": "^5.0.0",
|
"pestphp/pest-plugin-arch": "^5.0.0",
|
||||||
"pestphp/pest-plugin-mutate": "^5.0.1",
|
"pestphp/pest-plugin-mutate": "^5.0.2",
|
||||||
"pestphp/pest-plugin-profanity": "^5.0.0",
|
"pestphp/pest-plugin-profanity": "^5.0.0",
|
||||||
"phpunit/phpunit": "^13.3.0",
|
"phpunit/phpunit": "^13.3.0",
|
||||||
"symfony/process": "^8.1.0"
|
"symfony/process": "^8.1.0"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"pestphp/pest-dev-tools": "^5.0.0",
|
"pestphp/pest-dev-tools": "^5.0.0",
|
||||||
"pestphp/pest-plugin-browser": "^5.0.0",
|
"pestphp/pest-plugin-browser": "^5.0.1",
|
||||||
"pestphp/pest-plugin-phpstan": "^5.0.2",
|
"pestphp/pest-plugin-phpstan": "^5.0.2",
|
||||||
"pestphp/pest-plugin-rector": "^5.0.3",
|
"pestphp/pest-plugin-rector": "^5.0.3",
|
||||||
"pestphp/pest-plugin-type-coverage": "^5.0.2",
|
"pestphp/pest-plugin-type-coverage": "^5.0.2",
|
||||||
|
|||||||
+1
-1
@@ -37,9 +37,9 @@ final class Kernel
|
|||||||
private const array BOOTSTRAPPERS = [
|
private const array BOOTSTRAPPERS = [
|
||||||
Bootstrappers\BootOverrides::class,
|
Bootstrappers\BootOverrides::class,
|
||||||
Bootstrappers\BootPhpUnitConfiguration::class,
|
Bootstrappers\BootPhpUnitConfiguration::class,
|
||||||
Plugins\Tia\Bootstrapper::class,
|
|
||||||
Bootstrappers\BootSubscribers::class,
|
Bootstrappers\BootSubscribers::class,
|
||||||
Bootstrappers\BootFiles::class,
|
Bootstrappers\BootFiles::class,
|
||||||
|
Plugins\Tia\Bootstrapper::class,
|
||||||
Bootstrappers\BootView::class,
|
Bootstrappers\BootView::class,
|
||||||
Bootstrappers\BootKernelDump::class,
|
Bootstrappers\BootKernelDump::class,
|
||||||
Bootstrappers\BootExcludeList::class,
|
Bootstrappers\BootExcludeList::class,
|
||||||
|
|||||||
@@ -734,6 +734,12 @@ final class Expectation
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (! $snapshots->has()) {
|
if (! $snapshots->has()) {
|
||||||
|
if (! Snapshot::shouldCreateMissingSnapshots()) {
|
||||||
|
$filename = $snapshots->filename();
|
||||||
|
|
||||||
|
Assert::fail($message === '' ? "Snapshot is missing at [$filename]. Run Pest with --update-snapshots to create it." : $message);
|
||||||
|
}
|
||||||
|
|
||||||
$filename = $snapshots->save($string);
|
$filename = $snapshots->save($string);
|
||||||
|
|
||||||
TestSuite::getInstance()->registerSnapshotChange("Snapshot created at [$filename]");
|
TestSuite::getInstance()->registerSnapshotChange("Snapshot created at [$filename]");
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ namespace Pest;
|
|||||||
|
|
||||||
function version(): string
|
function version(): string
|
||||||
{
|
{
|
||||||
return '5.0.5';
|
return '5.1.1';
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDirectory(string $file = ''): string
|
function testDirectory(string $file = ''): string
|
||||||
|
|||||||
@@ -16,6 +16,36 @@ final class Snapshot implements HandlesArguments
|
|||||||
|
|
||||||
public static bool $updateSnapshots = false;
|
public static bool $updateSnapshots = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private const array CI_ENVIRONMENT_VARIABLES = [
|
||||||
|
'CI',
|
||||||
|
'GITHUB_ACTIONS',
|
||||||
|
'GITLAB_CI',
|
||||||
|
'CIRCLECI',
|
||||||
|
'TRAVIS',
|
||||||
|
'APPVEYOR',
|
||||||
|
'BITBUCKET_BUILD_NUMBER',
|
||||||
|
'BUILDKITE',
|
||||||
|
'TEAMCITY_VERSION',
|
||||||
|
'JENKINS_URL',
|
||||||
|
'SYSTEM_COLLECTIONURI',
|
||||||
|
'CI_NAME',
|
||||||
|
'TASKCLUSTER_ROOT_URL',
|
||||||
|
'DRONE',
|
||||||
|
'WERCKER',
|
||||||
|
'NEVERCODE',
|
||||||
|
'SEMAPHORE',
|
||||||
|
'NETLIFY',
|
||||||
|
'NOW_BUILDER',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function shouldCreateMissingSnapshots(): bool
|
||||||
|
{
|
||||||
|
return self::$updateSnapshots || ! self::runningOnCI();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@@ -119,4 +149,13 @@ final class Snapshot implements HandlesArguments
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function runningOnCI(): bool
|
||||||
|
{
|
||||||
|
if (Environment::name() === Environment::CI) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_any(self::CI_ENVIRONMENT_VARIABLES, fn (string $environmentVariable): bool => getenv($environmentVariable) !== false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,16 @@ use Pest\Support\Container;
|
|||||||
*/
|
*/
|
||||||
final class Configuration
|
final class Configuration
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function directory(string $directory): self
|
||||||
|
{
|
||||||
|
Storage::useDirectory($directory);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -85,6 +85,10 @@ final class JsModuleGraph
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! self::matchesDiskCasing($projectRoot, $rel)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (self::dirHasPageFile($abs)) {
|
if (self::dirHasPageFile($abs)) {
|
||||||
return $abs;
|
return $abs;
|
||||||
}
|
}
|
||||||
@@ -93,12 +97,30 @@ final class JsModuleGraph
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function matchesDiskCasing(string $projectRoot, string $relative): bool
|
||||||
|
{
|
||||||
|
$current = rtrim($projectRoot, DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
|
foreach (explode('/', $relative) as $segment) {
|
||||||
|
$entries = @scandir($current);
|
||||||
|
|
||||||
|
if ($entries === false || ! in_array($segment, $entries, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current .= DIRECTORY_SEPARATOR.$segment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static function dirHasPageFile(string $dir): bool
|
private static function dirHasPageFile(string $dir): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$iterator = new \RecursiveIteratorIterator(
|
$iterator = new \RecursiveIteratorIterator(
|
||||||
new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
|
new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
|
||||||
\RecursiveIteratorIterator::LEAVES_ONLY,
|
\RecursiveIteratorIterator::LEAVES_ONLY,
|
||||||
|
\RecursiveIteratorIterator::CATCH_GET_CHILD,
|
||||||
);
|
);
|
||||||
} catch (\UnexpectedValueException) {
|
} catch (\UnexpectedValueException) {
|
||||||
return false;
|
return false;
|
||||||
@@ -265,20 +287,25 @@ final class JsModuleGraph
|
|||||||
if ($jsRoot !== null && is_dir($jsRoot)) {
|
if ($jsRoot !== null && is_dir($jsRoot)) {
|
||||||
$entries = [];
|
$entries = [];
|
||||||
|
|
||||||
$iterator = new \RecursiveIteratorIterator(
|
try {
|
||||||
new \RecursiveDirectoryIterator($jsRoot, \FilesystemIterator::SKIP_DOTS),
|
$iterator = new \RecursiveIteratorIterator(
|
||||||
\RecursiveIteratorIterator::LEAVES_ONLY,
|
new \RecursiveDirectoryIterator($jsRoot, \FilesystemIterator::SKIP_DOTS),
|
||||||
);
|
\RecursiveIteratorIterator::LEAVES_ONLY,
|
||||||
|
\RecursiveIteratorIterator::CATCH_GET_CHILD,
|
||||||
|
);
|
||||||
|
|
||||||
/** @var \SplFileInfo $file */
|
/** @var \SplFileInfo $file */
|
||||||
foreach ($iterator as $file) {
|
foreach ($iterator as $file) {
|
||||||
if (! $file->isFile()) {
|
if (! $file->isFile()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entries[] = $file->getPathname()
|
||||||
|
.':'.$file->getSize()
|
||||||
|
.':'.$file->getMTime();
|
||||||
}
|
}
|
||||||
|
} catch (\UnexpectedValueException|\RuntimeException) {
|
||||||
$entries[] = $file->getPathname()
|
return null;
|
||||||
.':'.$file->getSize()
|
|
||||||
.':'.$file->getMTime();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sort($entries);
|
sort($entries);
|
||||||
|
|||||||
@@ -9,8 +9,17 @@ namespace Pest\Plugins\Tia;
|
|||||||
*/
|
*/
|
||||||
final class Storage
|
final class Storage
|
||||||
{
|
{
|
||||||
|
private static ?string $directory = null;
|
||||||
|
|
||||||
public static function tempDir(string $projectRoot): string
|
public static function tempDir(string $projectRoot): string
|
||||||
{
|
{
|
||||||
|
if (self::$directory !== null) {
|
||||||
|
$isAbsolute = str_starts_with(self::$directory, DIRECTORY_SEPARATOR)
|
||||||
|
|| preg_match('/^[a-z]:[\\\\\/]/i', self::$directory) === 1;
|
||||||
|
|
||||||
|
return $isAbsolute ? self::$directory : $projectRoot.DIRECTORY_SEPARATOR.self::$directory;
|
||||||
|
}
|
||||||
|
|
||||||
$home = self::homeDir();
|
$home = self::homeDir();
|
||||||
|
|
||||||
if ($home === null) {
|
if ($home === null) {
|
||||||
@@ -25,6 +34,11 @@ final class Storage
|
|||||||
.DIRECTORY_SEPARATOR.self::projectKey($projectRoot);
|
.DIRECTORY_SEPARATOR.self::projectKey($projectRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function useDirectory(?string $directory): void
|
||||||
|
{
|
||||||
|
self::$directory = $directory;
|
||||||
|
}
|
||||||
|
|
||||||
public static function purge(string $projectRoot): void
|
public static function purge(string $projectRoot): void
|
||||||
{
|
{
|
||||||
$dir = self::tempDir($projectRoot);
|
$dir = self::tempDir($projectRoot);
|
||||||
|
|||||||
@@ -56,7 +56,12 @@ final class SnapshotRepository
|
|||||||
|
|
||||||
file_put_contents($snapshotFilename, $snapshot);
|
file_put_contents($snapshotFilename, $snapshot);
|
||||||
|
|
||||||
return str_replace(dirname($this->testsPath).'/', '', $snapshotFilename);
|
return $this->filename();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filename(): string
|
||||||
|
{
|
||||||
|
return str_replace(dirname($this->testsPath).'/', '', $this->getSnapshotFilename());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function flush(): void
|
public function flush(): void
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
Pest Testing Framework 5.0.5.
|
Pest Testing Framework 5.1.1.
|
||||||
|
|
||||||
USAGE: pest <file> [options]
|
USAGE: pest <file> [options]
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
|
|
||||||
Pest Testing Framework 5.0.5.
|
Pest Testing Framework 5.1.1.
|
||||||
|
|
||||||
|
|||||||
@@ -1906,6 +1906,14 @@
|
|||||||
✓ does not throw when an integer --random-order-seed is passed as a separate argv element
|
✓ does not throw when an integer --random-order-seed is passed as a separate argv element
|
||||||
✓ still detects --tia when an integer argument is present
|
✓ still detects --tia when an integer argument is present
|
||||||
|
|
||||||
|
PASS Tests\Unit\Plugins\Tia\JsModuleGraph
|
||||||
|
✓ it accepts a page directory candidate only when every segment matches the casing on disk with dataset "exact"
|
||||||
|
✓ it accepts a page directory candidate only when every segment matches the casing on disk with dataset "wrong leaf"
|
||||||
|
✓ it accepts a page directory candidate only when every segment matches the casing on disk with dataset "wrong parent"
|
||||||
|
✓ it accepts a page directory candidate only when every segment matches the casing on disk with dataset "absent"
|
||||||
|
✓ it resolves the pages directory with the casing it has on disk
|
||||||
|
✓ it fingerprints a project whose js tree holds a directory it cannot open
|
||||||
|
|
||||||
PASS Tests\Unit\Plugins\Tia\Lockfiles\PackageLock
|
PASS Tests\Unit\Plugins\Tia\Lockfiles\PackageLock
|
||||||
✓ it applies only to package-lock.json
|
✓ it applies only to package-lock.json
|
||||||
✓ it returns null for contents that are not an npm lockfile
|
✓ it returns null for contents that are not an npm lockfile
|
||||||
@@ -1928,6 +1936,10 @@
|
|||||||
✓ it records assertions against the same per-dataset key
|
✓ it records assertions against the same per-dataset key
|
||||||
✓ it leaves a test without a dataset keyed by class and method
|
✓ it leaves a test without a dataset keyed by class and method
|
||||||
|
|
||||||
|
PASS Tests\Unit\Plugins\Tia\Storage
|
||||||
|
✓ it uses a project-relative configured directory
|
||||||
|
✓ it uses an absolute configured directory
|
||||||
|
|
||||||
PASS Tests\Unit\Plugins\Tia\TableExtractor
|
PASS Tests\Unit\Plugins\Tia\TableExtractor
|
||||||
✓ fromSql() → it extracts tables from plain DML
|
✓ fromSql() → it extracts tables from plain DML
|
||||||
✓ fromSql() → it extracts tables from joins
|
✓ fromSql() → it extracts tables from joins
|
||||||
@@ -2021,6 +2033,10 @@
|
|||||||
✓ it builds the expected alias map from a vite config with ('laravel-plugin-default')
|
✓ it builds the expected alias map from a vite config with ('laravel-plugin-default')
|
||||||
✓ it builds the expected alias map from a vite config with ('no-alias-no-plugin')
|
✓ it builds the expected alias map from a vite config with ('no-alias-no-plugin')
|
||||||
✓ it builds the expected alias map from a vite config with ('no-config')
|
✓ it builds the expected alias map from a vite config with ('no-config')
|
||||||
|
✓ it accepts a page directory candidate only when it matches the casing on disk with ('exact')
|
||||||
|
✓ it accepts a page directory candidate only when it matches the casing on disk with ('wrong leaf')
|
||||||
|
✓ it accepts a page directory candidate only when it matches the casing on disk with ('wrong parent')
|
||||||
|
✓ it accepts a page directory candidate only when it matches the casing on disk with ('absent')
|
||||||
|
|
||||||
PASS Tests\Unit\Preset
|
PASS Tests\Unit\Preset
|
||||||
✓ preset invalid name
|
✓ preset invalid name
|
||||||
@@ -2209,4 +2225,4 @@
|
|||||||
✓ pass with dataset with ('my-datas-set-value')
|
✓ pass with dataset with ('my-datas-set-value')
|
||||||
✓ within describe → pass with dataset with ('my-datas-set-value')
|
✓ within describe → pass with dataset with ('my-datas-set-value')
|
||||||
|
|
||||||
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1560 passed (3400 assertions)
|
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1572 passed (3413 assertions)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Plugins\Tia\JsModuleGraph;
|
||||||
|
|
||||||
|
function tiaJsModuleGraphCall(string $method, mixed ...$arguments): mixed
|
||||||
|
{
|
||||||
|
return new ReflectionMethod(JsModuleGraph::class, $method)->invoke(null, ...$arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tiaJsModuleGraphProject(): string
|
||||||
|
{
|
||||||
|
$root = sys_get_temp_dir().'/pest-tia-js-module-graph-'.bin2hex(random_bytes(6));
|
||||||
|
|
||||||
|
mkdir($root.'/resources/js/pages', 0755, true);
|
||||||
|
file_put_contents($root.'/vite.config.ts', "export default {}\n");
|
||||||
|
file_put_contents($root.'/resources/js/pages/Dashboard.vue', "<template>ok</template>\n");
|
||||||
|
|
||||||
|
return $root;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tiaJsModuleGraphRemove(string $path): void
|
||||||
|
{
|
||||||
|
if (! is_dir($path)) {
|
||||||
|
@unlink($path);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@chmod($path, 0755);
|
||||||
|
|
||||||
|
$entries = @scandir($path);
|
||||||
|
|
||||||
|
foreach ($entries === false ? [] : $entries as $entry) {
|
||||||
|
if ($entry === '.') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($entry === '..') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tiaJsModuleGraphRemove($path.'/'.$entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@rmdir($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function (): void {
|
||||||
|
$this->projectRoot = tiaJsModuleGraphProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function (): void {
|
||||||
|
tiaJsModuleGraphRemove($this->projectRoot);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a page directory candidate only when every segment matches the casing on disk', function (string $candidate, bool $expected): void {
|
||||||
|
expect(tiaJsModuleGraphCall('matchesDiskCasing', $this->projectRoot, $candidate))->toBe($expected);
|
||||||
|
})->with([
|
||||||
|
'exact' => ['resources/js/pages', true],
|
||||||
|
'wrong leaf' => ['resources/js/Pages', false],
|
||||||
|
'wrong parent' => ['Resources/js/pages', false],
|
||||||
|
'absent' => ['assets/js/pages', false],
|
||||||
|
]);
|
||||||
|
|
||||||
|
it('resolves the pages directory with the casing it has on disk', function (): void {
|
||||||
|
$expected = $this->projectRoot
|
||||||
|
.DIRECTORY_SEPARATOR.'resources'
|
||||||
|
.DIRECTORY_SEPARATOR.'js'
|
||||||
|
.DIRECTORY_SEPARATOR.'pages';
|
||||||
|
|
||||||
|
expect(tiaJsModuleGraphCall('firstExistingPagesDir', $this->projectRoot))->toBe($expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fingerprints a project whose js tree holds a directory it cannot open', function (): void {
|
||||||
|
$locked = $this->projectRoot.'/resources/js/locked';
|
||||||
|
|
||||||
|
mkdir($locked, 0755, true);
|
||||||
|
file_put_contents($locked.'/Secret.vue', "<template>ok</template>\n");
|
||||||
|
chmod($locked, 0000);
|
||||||
|
|
||||||
|
if (is_readable($locked)) {
|
||||||
|
$this->markTestSkipped('the current user reads directories regardless of their mode.');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(tiaJsModuleGraphCall('fingerprint', $this->projectRoot))->toBeString();
|
||||||
|
});
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Plugins\Tia\Bootstrapper;
|
||||||
|
use Pest\Plugins\Tia\Configuration;
|
||||||
|
use Pest\Plugins\Tia\Contracts\State;
|
||||||
|
use Pest\Plugins\Tia\FileState;
|
||||||
|
use Pest\Plugins\Tia\Storage;
|
||||||
|
use Pest\Support\Container;
|
||||||
|
use Pest\TestSuite;
|
||||||
|
|
||||||
|
afterEach(function (): void {
|
||||||
|
Storage::useDirectory(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses a project-relative configured directory', function (): void {
|
||||||
|
$container = new Container;
|
||||||
|
$testSuite = new TestSuite(sys_get_temp_dir(), 'tests');
|
||||||
|
$container->add(TestSuite::class, $testSuite);
|
||||||
|
|
||||||
|
(new Configuration)->directory('.pest/tia');
|
||||||
|
new Bootstrapper($container)->boot();
|
||||||
|
|
||||||
|
$state = $container->get(State::class);
|
||||||
|
|
||||||
|
if (! $state instanceof FileState) {
|
||||||
|
throw new RuntimeException('Expected the TIA state to use file storage.');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(Storage::tempDir('/project'))->toBe('/project'.DIRECTORY_SEPARATOR.'.pest/tia')
|
||||||
|
->and($state->pathFor('graph.json'))->toBe($testSuite->rootPath.DIRECTORY_SEPARATOR.'.pest/tia/graph.json');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses an absolute configured directory', function (): void {
|
||||||
|
(new Configuration)->directory('/tmp/pest-tia');
|
||||||
|
|
||||||
|
expect(Storage::tempDir('/project'))->toBe('/tmp/pest-tia');
|
||||||
|
});
|
||||||
@@ -357,6 +357,63 @@ function tiaViteAliasResults(): array
|
|||||||
return $cache = ['roots' => $roots, 'aliases' => $aliases];
|
return $cache = ['roots' => $roots, 'aliases' => $aliases];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tiaViteCasingFixtures(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'exact' => ['resources/js/pages', true],
|
||||||
|
'wrong leaf' => ['resources/js/Pages', false],
|
||||||
|
'wrong parent' => ['Resources/js/pages', false],
|
||||||
|
'absent' => ['assets/js/pages', false],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function tiaViteCasingResults(): array
|
||||||
|
{
|
||||||
|
static $cache = null;
|
||||||
|
if ($cache !== null) {
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
$root = sys_get_temp_dir().'/pest-tia-vite-casing-'.bin2hex(random_bytes(6));
|
||||||
|
mkdir($root.'/resources/js/pages', 0755, true);
|
||||||
|
file_put_contents($root.'/resources/js/pages/Dashboard.vue', "<template>ok</template>\n");
|
||||||
|
|
||||||
|
$helper = str_replace('\\', '/', tiaViteHelperPath());
|
||||||
|
$normalized = str_replace('\\', '/', $root);
|
||||||
|
|
||||||
|
$payload = [];
|
||||||
|
foreach (tiaViteCasingFixtures() as $name => [$candidate]) {
|
||||||
|
$payload[] = ['name' => $name, 'candidate' => $candidate];
|
||||||
|
}
|
||||||
|
|
||||||
|
$inputFile = tempnam(sys_get_temp_dir(), 'tia-vite-casing-');
|
||||||
|
file_put_contents($inputFile, json_encode($payload));
|
||||||
|
$input = str_replace('\\', '/', $inputFile);
|
||||||
|
|
||||||
|
$script = <<<JS
|
||||||
|
import { matchesDiskCasing } from '{$helper}'
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
const cases = JSON.parse(readFileSync('{$input}', 'utf8'))
|
||||||
|
const out = {}
|
||||||
|
for (const c of cases) out[c.name] = await matchesDiskCasing('{$normalized}', c.candidate)
|
||||||
|
process.stdout.write(JSON.stringify(out))
|
||||||
|
JS;
|
||||||
|
|
||||||
|
$process = new Process(['node', '--input-type=module', '-e', $script]);
|
||||||
|
$process->mustRun();
|
||||||
|
|
||||||
|
$results = json_decode($process->getOutput(), true, flags: JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
@unlink($inputFile);
|
||||||
|
@unlink($root.'/resources/js/pages/Dashboard.vue');
|
||||||
|
@rmdir($root.'/resources/js/pages');
|
||||||
|
@rmdir($root.'/resources/js');
|
||||||
|
@rmdir($root.'/resources');
|
||||||
|
@rmdir($root);
|
||||||
|
|
||||||
|
return $cache = $results;
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(function (): void {
|
beforeEach(function (): void {
|
||||||
if ((new ExecutableFinder)->find('node') === null) {
|
if ((new ExecutableFinder)->find('node') === null) {
|
||||||
$this->markTestSkipped('node is not available.');
|
$this->markTestSkipped('node is not available.');
|
||||||
@@ -409,3 +466,9 @@ it('builds the expected alias map from a vite config', function (string $name):
|
|||||||
|
|
||||||
expect($results['aliases'][$name])->toEqual($expected);
|
expect($results['aliases'][$name])->toEqual($expected);
|
||||||
})->with(array_keys(tiaViteAliasFixtures()));
|
})->with(array_keys(tiaViteAliasFixtures()));
|
||||||
|
|
||||||
|
it('accepts a page directory candidate only when it matches the casing on disk', function (string $name): void {
|
||||||
|
[, $expected] = tiaViteCasingFixtures()[$name];
|
||||||
|
|
||||||
|
expect(tiaViteCasingResults()[$name])->toBe($expected);
|
||||||
|
})->with(array_keys(tiaViteCasingFixtures()));
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ test('parallel', function () use ($run): void {
|
|||||||
$file = file_get_contents(__FILE__);
|
$file = file_get_contents(__FILE__);
|
||||||
$file = preg_replace(
|
$file = preg_replace(
|
||||||
'/\$expected = \'.*?\';/',
|
'/\$expected = \'.*?\';/',
|
||||||
"\$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1542 passed (3345 assertions)';",
|
"\$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1554 passed (3358 assertions)';",
|
||||||
$file,
|
$file,
|
||||||
);
|
);
|
||||||
file_put_contents(__FILE__, $file);
|
file_put_contents(__FILE__, $file);
|
||||||
}
|
}
|
||||||
|
|
||||||
$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1542 passed (3345 assertions)';
|
$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1554 passed (3358 assertions)';
|
||||||
|
|
||||||
expect($output)
|
expect($output)
|
||||||
->toContain("Tests: {$expected}")
|
->toContain("Tests: {$expected}")
|
||||||
|
|||||||
Reference in New Issue
Block a user