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
13 changes: 10 additions & 3 deletions src/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,13 +334,20 @@ function browsersWithBuckets(browserbuckets: string[]): [string, string][] {
// here (#927, ActivityWatch/activitywatch#1094). Fork alternatives are $-anchored so
// names like "archive" / "Dialog" don't match.
//
// When a dedicated fork bucket participates (today: settings-override Arc), only that
// fork is stripped from the chrome stream so the dedicated bucket owns those events
// without dropping other chrome-bucket forks (Dia has no dedicated bucket).
// Helium is not a Chromium fork itself, but it can run the Chrome Web Store
// extension build, which reports the "Helium" app name into the chrome bucket
// the same way (#898). It's listed here rather than in browser_appname_regex.helium
// so it gets the same dedicated-bucket exclusion as Arc/Dia below.
//
// When a dedicated fork bucket participates (today: settings-override Arc, or a
// standalone Helium bucket), only that fork is stripped from the chrome stream so
// the dedicated bucket owns those events without dropping other chrome-bucket
// forks (Dia has no dedicated bucket).
const CHROME_BASE_ALTS = ['google[-_ ]?chrome', 'chrome', 'chromium'];
const CHROME_FORK_ALTS: Record<string, string> = {
arc: 'arc(\\.exe)?$',
dia: 'dia(\\.exe)?$',
helium: 'helium(\\.exe)?$',
};

export function chromeAppnameRegex(excludeForks: Iterable<string> = []): string {
Expand Down
42 changes: 42 additions & 0 deletions test/unit/queries.test.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* Chromium forks that report through the chrome extension bucket (#927):
* 'Arc', 'arc.exe', 'Arc.exe', 'Dia', 'Dia.exe'
* (macOS bundle ID retained as exact: 'company.thebrowser.dia')
* Helium running the Chrome Web Store extension build (#898):
* 'Helium', 'helium.exe'
*
* Firefox: 'Firefox', 'Firefox.exe', 'firefox', 'firefox.exe',
* 'Firefox Developer Edition', 'firefoxdeveloperedition',
Expand Down Expand Up @@ -101,6 +103,9 @@ describe('browser_appname_regex', () => {
'Arc.exe',
'Dia',
'Dia.exe',
// Helium running the Chrome Web Store extension build (#898)
'Helium',
'helium.exe',
];
for (const name of knownNames) {
expect(re.test(name)).toBe(true);
Expand All @@ -118,6 +123,7 @@ describe('browser_appname_regex', () => {
expect(re.test('archive')).toBe(false);
expect(re.test('arcade')).toBe(false);
expect(re.test('Dialog')).toBe(false);
expect(re.test('Heliumburger')).toBe(false);
});

test('chrome exact list includes the Dia macOS bundle id', () => {
Expand All @@ -141,6 +147,12 @@ describe('browser_appname_regex', () => {
expect(both.test('Dia')).toBe(false);
expect(both.test('Chrome')).toBe(true);

const noHelium = toRegex(chromeAppnameRegex(['helium']));
expect(noHelium.test('Helium')).toBe(false);
expect(noHelium.test('helium.exe')).toBe(false);
expect(noHelium.test('Arc')).toBe(true);
expect(noHelium.test('Google Chrome')).toBe(true);

expect(chromeAppnameRegex()).toBe(browser_appname_regex.chrome);
});

Expand Down Expand Up @@ -301,6 +313,36 @@ describe('chrome fork matching in generated query', () => {
expect(query).toContain('dia(\\\\.exe)?$');
});

test('chrome-only bucket (no dedicated Helium bucket) still matches Helium app name (#898)', () => {
const query = fullDesktopQuery({
...params,
bid_browsers: ['aw-watcher-web-chrome_testhost'],
}).join('\n');
expect(query).toContain('helium(\\\\.exe)?$');
});

test('mixed chrome and Helium buckets: Helium bucket owns Helium events, chrome stream excludes Helium', () => {
const query = fullDesktopQuery({
...params,
bid_browsers: ['aw-watcher-web-chrome_testhost', 'aw-watcher-web-helium_testhost'],
}).join('\n');
const chromeWindowFilter = query.slice(
query.indexOf('window_chrome_re ='),
query.indexOf('events_chrome = filter_period_intersect')
);
// The chrome stream must NOT match Helium when a dedicated Helium bucket exists.
expect(chromeWindowFilter).not.toContain('helium(\\\\.exe)?$');
// Dia has no dedicated bucket and still writes to chrome — keep matching it.
expect(chromeWindowFilter).toContain('dia(\\\\.exe)?$');
// The Helium bucket keeps its own matching path.
expect(query).toContain('window_helium_re =');
expect(query).toContain('(?i)(helium)');
// Streams concat plainly; no overlap-masking that would drop real activity.
expect(query).toContain('browser_events = concat(browser_events, events_chrome);');
expect(query).toContain('browser_events = concat(browser_events, events_helium);');
expect(query).not.toContain('union_no_overlap');
});

test('mixed chrome and Arc buckets: Arc bucket owns Arc events, chrome stream excludes Arc', () => {
const query = fullDesktopQuery({
...params,
Expand Down
Loading