@wp-tester/phpunit 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Cache expiration constants for common use cases
3
+ */
4
+ export declare const CACHE_FOREVER = 0;
5
+ export declare const CACHE_DISABLED = -1;
6
+ export declare const CACHE_1_HOUR: number;
7
+ export declare const CACHE_1_DAY: number;
8
+ export declare const CACHE_1_WEEK: number;
9
+ export interface CacheFetchOptions {
10
+ /**
11
+ * Base directory for caching (defaults to ~/.wp-tester/cache)
12
+ */
13
+ baseCacheDir?: string;
14
+ /**
15
+ * Subdirectory within the cache (e.g., 'test-lib', 'wp-cli')
16
+ */
17
+ cacheKey: string;
18
+ /**
19
+ * URL to download from
20
+ */
21
+ url: string;
22
+ /**
23
+ * Cache expiration time in milliseconds (defaults to 24 hours)
24
+ * - Positive number: cache expires after that many milliseconds
25
+ * - 0 (CACHE_FOREVER): cache never expires
26
+ * - -1 (CACHE_DISABLED): always re-download, no caching
27
+ */
28
+ cacheExpiration?: number;
29
+ /**
30
+ * Maximum number of retry attempts
31
+ */
32
+ maxRetries?: number;
33
+ }
34
+ /**
35
+ * Download a file and cache it locally, with retry logic
36
+ *
37
+ * @param options - Configuration options for cache fetch
38
+ * @returns Path to the cached file
39
+ */
40
+ export declare function cacheFetch(options: CacheFetchOptions): Promise<string>;
41
+ //# sourceMappingURL=cache-fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-fetch.d.ts","sourceRoot":"","sources":["../src/cache-fetch.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,YAAY,QAAiB,CAAC;AAC3C,eAAO,MAAM,WAAW,QAAsB,CAAC;AAC/C,eAAO,MAAM,YAAY,QAA0B,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0D5E"}
@@ -0,0 +1,114 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as os from 'node:os';
4
+ import * as https from 'node:https';
5
+ import { pipeline } from 'node:stream/promises';
6
+ import { createWriteStream } from 'node:fs';
7
+ /**
8
+ * Cache expiration constants for common use cases
9
+ */
10
+ export const CACHE_FOREVER = 0;
11
+ export const CACHE_DISABLED = -1;
12
+ export const CACHE_1_HOUR = 60 * 60 * 1000;
13
+ export const CACHE_1_DAY = 24 * 60 * 60 * 1000;
14
+ export const CACHE_1_WEEK = 7 * 24 * 60 * 60 * 1000;
15
+ /**
16
+ * Download a file and cache it locally, with retry logic
17
+ *
18
+ * @param options - Configuration options for cache fetch
19
+ * @returns Path to the cached file
20
+ */
21
+ export async function cacheFetch(options) {
22
+ const { baseCacheDir = path.join(os.homedir(), '.wp-tester', 'cache'), cacheKey, url, cacheExpiration = CACHE_1_DAY, maxRetries = 1, } = options;
23
+ const cacheDir = path.join(baseCacheDir, cacheKey);
24
+ const cacheFilePath = path.join(cacheDir, 'download');
25
+ // Check if cached file exists and is still valid
26
+ if (fs.existsSync(cacheFilePath)) {
27
+ // If cacheExpiration is -1 (CACHE_DISABLED), always re-download
28
+ if (cacheExpiration === -1) {
29
+ // Delete existing cache
30
+ fs.unlinkSync(cacheFilePath);
31
+ }
32
+ // If cacheExpiration is 0 (CACHE_FOREVER), cache never expires
33
+ else if (cacheExpiration === 0) {
34
+ return cacheFilePath;
35
+ }
36
+ // For positive values, check if cache has expired
37
+ else {
38
+ const stats = fs.statSync(cacheFilePath);
39
+ const now = Date.now();
40
+ const fileAge = now - stats.mtimeMs;
41
+ if (fileAge < cacheExpiration) {
42
+ // Cache is still valid
43
+ return cacheFilePath;
44
+ }
45
+ // Cache has expired, delete it
46
+ fs.unlinkSync(cacheFilePath);
47
+ }
48
+ }
49
+ // Ensure cache directory exists
50
+ fs.mkdirSync(cacheDir, { recursive: true });
51
+ try {
52
+ await downloadFileWithRetry(url, cacheFilePath, maxRetries);
53
+ return cacheFilePath;
54
+ }
55
+ catch (error) {
56
+ // Clean up partial download and cache directory
57
+ if (fs.existsSync(cacheFilePath)) {
58
+ fs.unlinkSync(cacheFilePath);
59
+ }
60
+ if (fs.existsSync(cacheDir)) {
61
+ fs.rmSync(cacheDir, { recursive: true, force: true });
62
+ }
63
+ throw new Error(`Failed to download from ${url}: ${error.message}`);
64
+ }
65
+ }
66
+ /**
67
+ * Download a file from URL with redirect support
68
+ */
69
+ async function downloadFile(url, destPath) {
70
+ return new Promise((resolve, reject) => {
71
+ https.get(url, { headers: { 'User-Agent': 'wp-tester' } }, (response) => {
72
+ if (response.statusCode === 302 || response.statusCode === 301) {
73
+ // Follow redirect
74
+ const redirectUrl = response.headers.location;
75
+ if (!redirectUrl) {
76
+ reject(new Error('Redirect without location header'));
77
+ return;
78
+ }
79
+ downloadFile(redirectUrl, destPath).then(resolve).catch(reject);
80
+ return;
81
+ }
82
+ if (response.statusCode !== 200) {
83
+ reject(new Error(`Download failed: ${response.statusCode}`));
84
+ return;
85
+ }
86
+ const fileStream = createWriteStream(destPath);
87
+ pipeline(response, fileStream)
88
+ .then(() => resolve())
89
+ .catch(reject);
90
+ }).on('error', reject);
91
+ });
92
+ }
93
+ /**
94
+ * Download file with retry logic and exponential backoff
95
+ */
96
+ async function downloadFileWithRetry(url, destPath, maxRetries) {
97
+ let lastError = null;
98
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
99
+ try {
100
+ await downloadFile(url, destPath);
101
+ return; // Success
102
+ }
103
+ catch (error) {
104
+ lastError = error;
105
+ if (attempt < maxRetries - 1) {
106
+ // Exponential backoff: 1s, 2s, 4s
107
+ const delay = Math.pow(2, attempt) * 1000;
108
+ await new Promise(resolve => setTimeout(resolve, delay));
109
+ }
110
+ }
111
+ }
112
+ throw lastError || new Error('Download failed after retries');
113
+ }
114
+ //# sourceMappingURL=cache-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-fetch.js","sourceRoot":"","sources":["../src/cache-fetch.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * Provides PHPUnit test execution and result parsing for wp-tester.
5
5
  */
6
- export { runPhpUnitTests, shouldRunPhpUnitTests } from "./runner.js";
6
+ export { runPhpunitTests, shouldRunPhpunitTests } from "./runner.js";
7
7
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * Provides PHPUnit test execution and result parsing for wp-tester.
5
5
  */
6
- export { runPhpUnitTests, shouldRunPhpUnitTests } from "./runner.js";
6
+ export { runPhpunitTests, shouldRunPhpunitTests } from "./runner.js";
7
7
  //# sourceMappingURL=index.js.map
package/dist/runner.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type { WPTesterConfig } from "@wp-tester/config";
2
2
  import type { Report } from "@wp-tester/results";
3
- export declare function shouldRunPhpUnitTests(config: WPTesterConfig): boolean;
3
+ export declare function shouldRunPhpunitTests(config: WPTesterConfig): boolean;
4
4
  /**
5
5
  * Run PHPUnit tests in WordPress Playground environment
6
6
  *
7
7
  * @param config - Test configuration or path to config file
8
8
  * @returns CTRF report with test results
9
9
  */
10
- export declare function runPhpUnitTests(config: WPTesterConfig | string): Promise<Report>;
10
+ export declare function runPhpunitTests(config: WPTesterConfig | string): Promise<Report>;
11
11
  //# sourceMappingURL=runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA+C,MAAM,mBAAmB,CAAC;AAErG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAQjD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAErE;AA0ID;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,GAAG,MAAM,GAC9B,OAAO,CAAC,MAAM,CAAC,CA6CjB"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA+C,MAAM,mBAAmB,CAAC;AAOrG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAMjD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAErE;AAoOD;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,GAAG,MAAM,GAC9B,OAAO,CAAC,MAAM,CAAC,CA6CjB"}
package/dist/runner.js CHANGED
@@ -1,10 +1,9 @@
1
- import { resolveConfig, parseBootstrapPath, hostToVfs } from "@wp-tester/config";
2
- import { EMPTY_REPORT, mergeReports } from "@wp-tester/results";
1
+ import { resolveConfig, parseBootstrapPath, hostToVfs, resolveAbsolute, } from "@wp-tester/config";
2
+ import { EMPTY_REPORT, mergeReports, StreamingReporter, TeamCityParser } from "@wp-tester/results";
3
3
  import { startPlayground } from "@wp-tester/runtime";
4
- import { parseJUnitXml } from "./junit-parser.js";
4
+ import { mountWordPressTestLibrary } from "./wordpress-test-lib.js";
5
5
  import { access } from "fs/promises";
6
- import { join } from "path";
7
- export function shouldRunPhpUnitTests(config) {
6
+ export function shouldRunPhpunitTests(config) {
8
7
  return config.tests?.phpunit !== undefined;
9
8
  }
10
9
  /**
@@ -15,17 +14,21 @@ export function shouldRunPhpUnitTests(config) {
15
14
  * @param hostPhpunitConfigPath - Absolute path to PHPUnit config on host
16
15
  * @returns CTRF report with test results
17
16
  */
18
- async function runPhpUnitTestsForEnvironment(config, environment, hostPhpunitConfigPath) {
19
- const runtime = await startPlayground(environment);
20
- const playground = runtime.playground;
21
- // Get testMode from config (defaults to "unit")
22
- const testMode = (config.tests.phpunit?.testMode ?? "unit");
23
- if (config.reporters && config.reporters.includes("default")) {
24
- const modeLabel = testMode === "unit"
25
- ? "unit tests (without WordPress)"
26
- : "integration tests (with WordPress)";
27
- console.log(`Running PHPUnit ${modeLabel} for environment: ${environment.name}`);
17
+ async function runPhpunitTestsForEnvironment(config, environment, hostPhpunitConfigPath) {
18
+ const testMode = config.tests.phpunit.testMode;
19
+ let phpUnitEnvironmentVariables = {};
20
+ let environmentWithMount = environment;
21
+ // Prepare environment with WordPress test library for unit tests only
22
+ // Integration tests use standard WordPress installation via wp-load.php
23
+ if (testMode === "unit") {
24
+ environmentWithMount = await mountWordPressTestLibrary(environment);
25
+ phpUnitEnvironmentVariables = {
26
+ WP_TESTS_DIR: "/tmp/wordpress-tests-lib",
27
+ };
28
28
  }
29
+ // Start playground with all mounts and steps including test library initialization
30
+ const runtime = await startPlayground(environmentWithMount);
31
+ const playground = runtime.playground;
29
32
  try {
30
33
  // Determine the plugin or theme being tested
31
34
  if (!config.projectType) {
@@ -38,6 +41,8 @@ async function runPhpUnitTestsForEnvironment(config, environment, hostPhpunitCon
38
41
  }
39
42
  // Map host PHPUnit config path to VFS
40
43
  const vfsPhpunitConfigPath = hostToVfs(hostPhpunitConfigPath, config);
44
+ // Map host PHPUnit executable path to VFS
45
+ const vfsPhpunitPath = hostToVfs(config.tests.phpunit.phpunitPath, config);
41
46
  // Only create WordPress bootstrap in integration mode
42
47
  let bootstrapFilePath = null;
43
48
  if (testMode === "integration") {
@@ -47,7 +52,7 @@ async function runPhpUnitTestsForEnvironment(config, environment, hostPhpunitCon
47
52
  let userBootstrap = `${config.projectVFSPath}/tests/bootstrap.php`; // Default fallback
48
53
  if (bootstrapPath) {
49
54
  // Bootstrap path from parseBootstrapPath is relative, convert to absolute
50
- const absoluteBootstrapPath = join(config.projectHostPath, bootstrapPath);
55
+ const absoluteBootstrapPath = resolveAbsolute(bootstrapPath, config.projectHostPath);
51
56
  userBootstrap = hostToVfs(absoluteBootstrapPath, config);
52
57
  }
53
58
  // Create a custom bootstrap file that loads WordPress, then user's bootstrap if it exists
@@ -65,50 +70,105 @@ async function runPhpUnitTestsForEnvironment(config, environment, hostPhpunitCon
65
70
  }
66
71
  `);
67
72
  }
68
- // npx @php-wasm/cli@1.1.2 ./vendor/bin/phpunit
69
- const logFilePath = `/tmp/phpunit-results-${Date.now()}.xml`;
70
- // Build PHP CLI arguments
73
+ // Build PHP CLI arguments with environment variables
74
+ // Use TeamCity format for streaming output
71
75
  const cliArgs = [
72
76
  "php",
73
- `${config.projectVFSPath}/vendor/bin/phpunit`,
77
+ // Set variables_order to EGPCS to ensure environment variables are accessible.
78
+ // This is required for WordPress test library to access WP_TESTS_DIR via getenv().
79
+ // Default PHP settings often exclude 'E' (Environment), which would break test setup.
80
+ "-d",
81
+ "variables_order=EGPCS",
82
+ vfsPhpunitPath,
74
83
  "-c",
75
84
  vfsPhpunitConfigPath,
76
- "--log-junit",
77
- logFilePath
85
+ "--teamcity", // Use TeamCity format for streaming
78
86
  ];
79
87
  // Override bootstrap file with our custom one (only in integration mode)
80
88
  if (bootstrapFilePath !== null) {
81
89
  cliArgs.push("--bootstrap", bootstrapFilePath);
82
90
  }
83
- // Run PHPUnit tests using WP-CLI
84
- const result = await playground.cli(cliArgs);
85
- if (!config.reporters || config.reporters.includes("default")) {
86
- await result.stdout.pipeTo(new WritableStream({
87
- write(chunk) {
88
- process.stdout.write(chunk);
89
- }
90
- }));
91
- }
92
- else {
93
- // Still need to consume the stream even if not displaying
94
- await result.stdout.cancel();
91
+ // Append additional PHPUnit arguments from config
92
+ const phpunitArgs = config.tests.phpunit.phpunitArgs;
93
+ if (phpunitArgs && phpunitArgs.length > 0) {
94
+ cliArgs.push(...phpunitArgs);
95
95
  }
96
+ // Create streaming reporter
97
+ // Disable summary since the CLI will print a combined summary
98
+ const useStreaming = config.reporters?.includes("default") ?? true;
99
+ const reporter = new StreamingReporter({
100
+ enabled: useStreaming,
101
+ showSummary: false,
102
+ });
103
+ // Signal test run start to initialize timing
104
+ reporter.onEvent({
105
+ type: "run:start",
106
+ toolName: "wp-tester-phpunit",
107
+ });
108
+ reporter.onEvent({
109
+ type: "suite:start",
110
+ name: `PHPUnit ${testMode} tests - '${environment.name}'`,
111
+ });
112
+ // Create TeamCity parser connected to the reporter
113
+ const parser = TeamCityParser.withReporter(reporter);
114
+ // Run PHPUnit tests
115
+ const result = await playground.cli(cliArgs, {
116
+ env: phpUnitEnvironmentVariables,
117
+ });
118
+ // Process stdout and stderr with TeamCity parser for streaming results
119
+ // PHPUnit outputs TeamCity format to both stdout and stderr
120
+ const textDecoder = new TextDecoder();
121
+ const stderrDecoder = new TextDecoder();
122
+ await Promise.all([
123
+ result.stdout.pipeTo(new WritableStream({
124
+ write(chunk) {
125
+ const text = textDecoder.decode(chunk, { stream: true });
126
+ parser.write(text);
127
+ },
128
+ close() {
129
+ parser.flush();
130
+ },
131
+ })),
132
+ result.stderr.pipeTo(new WritableStream({
133
+ write(chunk) {
134
+ const text = stderrDecoder.decode(chunk, { stream: true });
135
+ // Try to parse as TeamCity format first
136
+ parser.write(text);
137
+ // If streaming is disabled, also write non-TeamCity lines to stderr
138
+ // This ensures error messages still appear
139
+ if (!useStreaming) {
140
+ const lines = text.split("\n");
141
+ for (const line of lines) {
142
+ if (!line.trim().startsWith("##teamcity[")) {
143
+ process.stderr.write(line + "\n");
144
+ }
145
+ }
146
+ }
147
+ },
148
+ close() {
149
+ parser.flush();
150
+ },
151
+ })),
152
+ ]);
153
+ // Close the outer suite that was started earlier
154
+ reporter.onEvent({
155
+ type: "suite:end",
156
+ name: `PHPUnit ${testMode} tests - '${environment.name}'`,
157
+ });
158
+ // Signal test run end to finalize timing
159
+ reporter.onEvent({
160
+ type: "run:end",
161
+ });
96
162
  const exitCode = await result.exitCode;
97
163
  if (exitCode !== 0 && exitCode !== 1 && exitCode !== 2) {
98
164
  // Exit code 1 indicates test failures, exit code 2 indicates errors - both are acceptable
99
165
  console.error(`PHPUnit exited with code ${exitCode}`);
100
166
  return EMPTY_REPORT;
101
167
  }
102
- // Check if the log file was created
103
- if (!await playground.fileExists(logFilePath)) {
104
- console.error(`PHPUnit log file not created at ${logFilePath}`);
105
- return EMPTY_REPORT;
106
- }
107
- // Read the JUnit XML log file
108
- const xmlString = await playground.readFileAsText(logFilePath);
109
- // Parse the JUnit XML output to CTRF format
110
- const environmentName = environment.name;
111
- const report = await parseJUnitXml(xmlString, environmentName);
168
+ // Get the report from the streaming reporter
169
+ const report = reporter.getReport();
170
+ // Update tool name with environment
171
+ report.results.tool.name = `wp-tester-phpunit (${environment.name})`;
112
172
  return report;
113
173
  }
114
174
  catch (error) {
@@ -116,8 +176,15 @@ async function runPhpUnitTestsForEnvironment(config, environment, hostPhpunitCon
116
176
  return EMPTY_REPORT;
117
177
  }
118
178
  finally {
119
- // Cleanup
120
- runtime.server.close();
179
+ // Cleanup - ensure server closes even on errors
180
+ try {
181
+ await new Promise((resolve) => {
182
+ runtime.server.close(() => resolve());
183
+ });
184
+ }
185
+ catch (error) {
186
+ console.error("Error closing server:", error);
187
+ }
121
188
  }
122
189
  }
123
190
  /**
@@ -126,11 +193,11 @@ async function runPhpUnitTestsForEnvironment(config, environment, hostPhpunitCon
126
193
  * @param config - Test configuration or path to config file
127
194
  * @returns CTRF report with test results
128
195
  */
129
- export async function runPhpUnitTests(config) {
196
+ export async function runPhpunitTests(config) {
130
197
  // Resolve config (loads from path if string, resolves paths)
131
198
  const resolvedConfig = await resolveConfig(config);
132
199
  // Check if PHPUnit tests are configured
133
- if (!shouldRunPhpUnitTests(resolvedConfig)) {
200
+ if (!shouldRunPhpunitTests(resolvedConfig)) {
134
201
  return Promise.resolve(EMPTY_REPORT);
135
202
  }
136
203
  // Get PHPUnit config path from resolved config (already absolute after resolveConfig)
@@ -146,7 +213,7 @@ export async function runPhpUnitTests(config) {
146
213
  // Run tests for all environments
147
214
  const reports = [];
148
215
  for (const environment of resolvedConfig.environments) {
149
- const report = await runPhpUnitTestsForEnvironment(resolvedConfig, environment, hostPhpunitConfigPath);
216
+ const report = await runPhpunitTestsForEnvironment(resolvedConfig, environment, hostPhpunitConfigPath);
150
217
  if (report.results.summary.tests > 0) {
151
218
  reports.push(report);
152
219
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}