Skip to content

feat(experimental): implement ability to write test dependencies#1137

Merged
KuznetsovRoman merged 1 commit into
masterfrom
TESTPLANE-668.selectivity_p1_write
Oct 20, 2025
Merged

feat(experimental): implement ability to write test dependencies#1137
KuznetsovRoman merged 1 commit into
masterfrom
TESTPLANE-668.selectivity_p1_write

Conversation

@KuznetsovRoman

Copy link
Copy Markdown
Member

No description provided.

@pkg-pr-new

pkg-pr-new Bot commented Sep 18, 2025

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/gemini-testing/testplane@1137

commit: 6a85f19

this._sourceRoot = sourceRoot;
}

private _processStyle({ header: { styleSheetId, sourceURL, sourceMapURL } }: CssEvents["styleSheetAdded"]): void {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handles emitted "CSS.styleSheetAdded" events

import fs from "node:fs";

export class FileHashProvider {
private static readonly _hashStore: Map<string, Promise<string>> = new Map();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assuming hash for file can't be changed during Testplane launch

import { shallowSortObject } from "./utils";
import type { NormalizedDependencies } from "./types";

export class FileHashWriter {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tracks up-to-date hashes for all files:
image

Comment on lines +25 to +26
if (!cdpTargetId) {
throw new Error(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think this error could ever be thrown, though

this._sourceRoot = sourceRoot;
}

private _processScript({ scriptId, url, sourceMapURL }: DebuggerEvents["scriptParsed"]): void {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handles emitted "scriptParsed" events

};

const ensurePosixRelativeDependencyPathExists = memoize((posixRelativePath: string): void => {
const relativePath = posixRelativePath.replaceAll(path.posix.sep, path.sep);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

source map sources are posix paths (use "/" as separator even on windows)

const modules: string[] = [];

const classifyDependency = (dependency: string, typedResultArray: string[]): void => {
dependency = decodeURIComponent(softFileURLToPath(dependency));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"dependency" from source-map "sources" are url-like
So it can have "file://" protocol. It also can be relative path with urlencoded symbols

throw new Error(`Selectivity: Found unsupported protocol in dependencies ("${dependency}")`);
}

const dependencyRelativePath = path.posix.relative(path.posix.resolve(), path.posix.resolve(dependency));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • using "path.posix.resolve()" to get posix cwd
  • "path.posix.resolve(dependency)" resolves absolute path from cwd

Comment on lines +175 to +176
let cssIndex = 0;
let jsIndex = 0;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using 2 pointers to create merged "modules" array of uniq modules from sorted "css" and "js" arrays

Comment on lines +199 to +200
// Ensures file consistency
export const shallowSortObject = (obj: Record<string, unknown>): void => {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used before writing object to file system to ensure keys order is stable

}

const hash = new Promise<string>((resolve, reject) => {
const hash = crypto.createHash("md5");

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using md5 hashes as we dont need hash to be crypto-strong, and md5 is fast

@KuznetsovRoman KuznetsovRoman force-pushed the TESTPLANE-668.selectivity_p1_write branch from 862d56a to 646bf8d Compare September 19, 2025 00:33
@KuznetsovRoman KuznetsovRoman force-pushed the TESTPLANE-668.selectivity_p1_write branch 3 times, most recently from db6f13c to b1016a2 Compare October 17, 2025 13:51

@shadowusr shadowusr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most important thing is the docs on how this feature can be used along with complete example how this solves user's scenario. I think this is very important. Without docs and examples, I think its hardly possible to use.

A nice addition overall! 🔥

Comment thread src/browser/cdp/selectivity/utils.ts Outdated
sourceMapsEndIndex,
);

const isSourceMapEmbedded = new URL(sourceMapURL).protocol === "data:";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm wouldn't this throw if sourceMapURL is an absolute or relative path? Or it's guaranteed to have an URL format?

@KuznetsovRoman KuznetsovRoman Oct 20, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By specification, its url-like:

The sources field is a list of original sources used by the mappings field. Each entry is either a string that is a (potentially relative) URL or null if the source name is not known

https://tc39.es/ecma426/#sec-source-map-format

}

if (!browser.cdp) {
throw new Error("Selectivity: Devtools connection is not established, couldn't record selectivity without it");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not critical: when can this happen and why exactly? can we give some advice here?

@KuznetsovRoman KuznetsovRoman Oct 20, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when can this happen and why exactly?

No idea

not critical: when can this happen and why exactly? can we give some advice here?

No, since we dont know, why cdp connection might not be established, while we see the cases

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understood, that hashes.json file can be used to detect which files changed, fast. But I don't see how it's used later. Is it used anywhere? Can we leave a short comment here about the purpose of this class?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in part 1 (this pr), we only write dependencies, and dont read them

"read deps" + "skip unchanged" is part 2

Comment thread src/config/types.ts
reuseExisting: boolean;
};

selectivity: {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type has the same issue as many other sections in our config — can't just enable it and be fine with defaults. It forces me to specify all of the fields, even though they have defaults and it shouldn't.

Image

return sourceMap;
};

export const extractSourceFilesDeps = async (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not critical — some of these helpers contain a pretty complicated logic and just judging by their name it's hard to understand what exactly they do. I would add an explanatory comment like

Given compiled code, its source map, and the executed offsets, it returns the original source files touched — useful for turning coverage ranges into real TS/JS module dependencies.

@KuznetsovRoman KuznetsovRoman force-pushed the TESTPLANE-668.selectivity_p1_write branch from b1016a2 to 6a85f19 Compare October 20, 2025 22:11
@KuznetsovRoman KuznetsovRoman merged commit d8c5eaa into master Oct 20, 2025
5 checks passed
@KuznetsovRoman KuznetsovRoman deleted the TESTPLANE-668.selectivity_p1_write branch October 20, 2025 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants