[Feature]: `snapshotPathTemplate` as optional function
🚀 Feature Request
I wish to support the following feature in config:
snapshotPathTemplate?: string | ((testInfo: TestInfo) => string)
OR
snapshotPathResolver?: (filename: string, testInfo: TestInfo) => string
snapshotPathResolver should be invoked with the parsed snapshot path (=filename) for further handling, returning the final path to be used.
Currently I am patching playwright to support this. As you can see it is a very simple change.
diff --git a/node_modules/playwright/.DS_Store b/node_modules/playwright/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/node_modules/playwright/.DS_Store differ
diff --git a/node_modules/playwright/lib/common/config.js b/node_modules/playwright/lib/common/config.js
index 4b21d26..690c308 100644
--- a/node_modules/playwright/lib/common/config.js
+++ b/node_modules/playwright/lib/common/config.js
@@ -158,6 +158,7 @@ class FullProjectInternal {
const testDir = takeFirst(pathResolve(configDir, projectConfig.testDir), pathResolve(configDir, config.testDir), fullConfig.configDir);
const defaultSnapshotPathTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}';
this.snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate, defaultSnapshotPathTemplate);
+ this.snapshotPathResolver = takeFirst(projectConfig.snapshotPathResolver, config.snapshotPathResolver, p => p);
this.project = {
grep: takeFirst(projectConfig.grep, config.grep, defaultGrep),
grepInvert: takeFirst(projectConfig.grepInvert, config.grepInvert, null),
diff --git a/node_modules/playwright/lib/worker/testInfo.js b/node_modules/playwright/lib/worker/testInfo.js
index 5ed9668..61bce7f 100644
--- a/node_modules/playwright/lib/worker/testInfo.js
+++ b/node_modules/playwright/lib/worker/testInfo.js
@@ -386,7 +386,8 @@ class TestInfoImpl {
const parsedRelativeTestFilePath = _path.default.parse(relativeTestFilePath);
const projectNamePathSegment = (0, _utils.sanitizeForFilePath)(this.project.name);
const snapshotPath = (this._projectInternal.snapshotPathTemplate || '').replace(/\{(.)?testDir\}/g, '$1' + this.project.testDir).replace(/\{(.)?snapshotDir\}/g, '$1' + this.project.snapshotDir).replace(/\{(.)?snapshotSuffix\}/g, this.snapshotSuffix ? '$1' + this.snapshotSuffix : '').replace(/\{(.)?testFileDir\}/g, '$1' + parsedRelativeTestFilePath.dir).replace(/\{(.)?platform\}/g, '$1' + process.platform).replace(/\{(.)?projectName\}/g, projectNamePathSegment ? '$1' + projectNamePathSegment : '').replace(/\{(.)?testName\}/g, '$1' + this._fsSanitizedTestName()).replace(/\{(.)?testFileName\}/g, '$1' + parsedRelativeTestFilePath.base).replace(/\{(.)?testFilePath\}/g, '$1' + relativeTestFilePath).replace(/\{(.)?arg\}/g, '$1' + _path.default.join(parsedSubPath.dir, parsedSubPath.name)).replace(/\{(.)?ext\}/g, parsedSubPath.ext ? '$1' + parsedSubPath.ext : '');
- return _path.default.normalize(_path.default.resolve(this._configInternal.configDir, snapshotPath));
+ const resolvedSnapshotPath = this._projectInternal.snapshotPathResolver ? this._projectInternal.snapshotPathResolver(snapshotPath, this) : snapshotPath;
+ return _path.default.normalize(_path.default.resolve(this._configInternal.configDir, resolvedSnapshotPath));
}
skip(...args) {
this._modifier('skip', args);
Example
It should be used for fine grained runtime control over the snapshot path.
snapshotPathResolver: (file: string, testInfo: TestInfo) => {
if (CI) {
return file;
}
const { name, ext, dir } = path.parse(file);
const resolved = path.join(dir, `${name}.${process.platform}${ext}`);
if (existsSync(path.resolve(resolved))) {
console.warn(`Using local snapshot ${resolved}`);
return resolved;
}
return file;
}
Motivation
In a project I work on some snapshots fail on CI (a very little number) due to OS diffs and we do not want to handle 2 sets of snapshot for dev and for CI so I would like to snapshot locally against a different snapshot
@ShaMan123 For your specific case, I'd recommend to just ignore these specific snapshots when running locally. If you ever have a lot of snapshots that differ between CI and local, look into ignoreSnapshots option. That said, I will leave this feature request open, just in case it turns out to be a popular ask with various usecases.
@dgozman came looking for this, my usecase is simplifying migration from our current custom jest-based testing setup that uses jest-image-snapshot. Being able to configure the smallest details of this would help a lot with testing out whether a migration can work.
There are some differences between how jest-image-snapshot and playwright generate the snapshot paths, namely:
- Given a test file name of
badge.browser.tsx, jest-image-snapshot replaces dots with dashes, e.g. the resulting file name isbadge-browser-tsx-[...testName], whereas playwright keeps the dots, e.g. the resulting file name isbadge.browser.tsx-[...testName] - jest-image-snapshot lowercases the test.describe and test names, but playwright does not. So, given a
test.describe('Badge')andtest('renders Badge variants'), jest-image-snapshot generates a filename of[...testFileName]-badge-renders-badge-variants-1.png, whereas playwright keeps the uppercase letters in place, generating a filename of[testFileName]-Badge-renders-Badge-variants-1.png.- mind you, given that uppercase/lowercase letters in filenames behave differently across operating systems (some think they are equivalent, some think they are distinct), it may be a good idea to always lowercase?
Of course we can work around this by renaming the old snapshots first, but I'd guess making such migrations easier would benefit playwright as well.
Why was this issue closed?
Thank you for your involvement. This issue was closed due to limited engagement (upvotes/activity), lack of recent activity, and insufficient actionability. To maintain a manageable database, we prioritize issues based on these factors.
If you disagree with this closure, please open a new issue and reference this one. More support or clarity on its necessity may prompt a review. Your understanding and cooperation are appreciated.
Please reopen => https://github.com/Azure/azure-sdk-for-js/pull/35996
https://github.com/Azure/azure-sdk-for-js/pull/35997
For remote browsers:
snapshotPathTemplate: (...args) =>
process.env['_MPT_CLOUD_HOSTED_BROWSER_USED'] ?
remoteBrowserTemplateUsingRemoteBrowserPlatformToken :
defaultTemplate