Skip to content
Merged
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
36 changes: 35 additions & 1 deletion packages/app/tests/e2e/components/ChartEditorComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
* Used for creating and configuring dashboard tiles and chart explorer
*/
import { DisplayType } from '@hyperdx/common-utils/dist/types';
import { Locator, Page } from '@playwright/test';
import { expect, Locator, Page } from '@playwright/test';

import { dismissSqlAutocomplete, getSqlEditor } from '../utils/locators';
import { switchWhereToLucene } from '../utils/lucene-autocomplete';

import { WebhookAlertModalComponent } from './WebhookAlertModalComponent';

Expand Down Expand Up @@ -100,6 +101,39 @@ export class ChartEditorComponent {
await dismissSqlAutocomplete(this.page);
}

/**
* The tile-editor modal. Series-level lookups have to be scoped to it: the
* dashboard behind it renders its own WHERE input with identical markup.
* Only meaningful on a dashboard — the chart explorer has no modal.
*/
private get tileEditor() {
return this.page
.getByRole('dialog')
.filter({ has: this.page.getByTestId('chart-name-input') });
}

/**
* Set the tile's own filter, which a time chart stores as its series'
* `aggCondition` rather than in the config's statement-level `where`.
*
* Switches to Lucene first for two reasons: the mode is sticky (it is kept in
* localStorage, so a previous spec can leave it on SQL), and
* `series-where-input` exists only on the Lucene branch of SearchWhereInput —
* the SQL branch renders a CodeMirror that carries no test id.
*/
async setSeriesWhere(condition: string) {
const editor = this.tileEditor;
await switchWhereToLucene(editor.getByTestId('where-language-switch'));

const input = editor.getByTestId('series-where-input');
await expect(input).toBeVisible();
await input.fill(condition);
// Blur to close the suggestion dropdown, which otherwise overlays the
// Run/Save buttons and fails their actionability check. Escape would bubble
// to the tile-editor modal and close it.
await input.blur();
}

/**
* Select a data source
*/
Expand Down
29 changes: 29 additions & 0 deletions packages/app/tests/e2e/features/release-markers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,35 @@ test.describe('Release markers', { tag: ['@full-stack', '@dashboard'] }, () => {
await expect(page).not.toHaveURL(/releaseMarkers=true/);
});

// The test above filters the *dashboard*; this one filters the *tile*. They
// are different fields — a dashboard filter travels as `filters`, while a
// time chart's own filter is stored as its series' `aggCondition`, not in the
// config's statement-level `where` (the editor clears that). Reading only
// `where` left every time chart's markers unscoped, and because the resulting
// markers then spanned two services with no matching line, they were all
// suppressed: filter to one service, see nothing.
test("scopes markers to the tile's own filter", async ({ page }) => {
await seedReleases();

await dashboardPage.createNewDashboard();
await dashboardPage.addTileWithSource(
'Tile-filtered markers',
DEFAULT_LOGS_SOURCE_NAME,
`ServiceName:"${RELEASE_SERVICE}"`,
);
// No dashboard-wide filter this time: the tile's own filter has to be what
// narrows the releases query.
await dashboardPage.toggleReleaseAnnotations();

await expect(page).toHaveURL(/releaseMarkers=true/);

const labels = dashboardPage.getAnnotationLabels();
await expect(dashboardPage.getAnnotationMarkers()).toHaveCount(2);
await expect(labels.filter({ hasText: OLD_VERSION })).toBeVisible();
await expect(labels.filter({ hasText: NEW_VERSION })).toBeVisible();
await expect(labels.filter({ hasText: OTHER_VERSION })).toHaveCount(0);
});

// Colour alone can't identify a service once the legend overflows its 4-item
// cap and the matching entry hides behind "+N more". Hover has to name the
// service outright.
Expand Down
13 changes: 12 additions & 1 deletion packages/app/tests/e2e/page-objects/DashboardPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,13 +589,24 @@ export class DashboardPage {
* Add a tile bound to an explicit source. Unlike addTileWithConfig (which
* relies on the editor's default source), this selects a known source so the
* exported tile carries a deterministic source name that auto-maps on import.
*
* `seriesWhere` sets the tile's own filter, which a time chart stores as its
* series' `aggCondition` — a different field from the dashboard-wide filter
* that `setGlobalFilter` drives.
*/
async addTileWithSource(chartName: string, sourceName: string) {
async addTileWithSource(
chartName: string,
sourceName: string,
seriesWhere?: string,
) {
await this.addTile();
await expect(this.chartEditor.nameInput).toBeVisible();
await this.chartEditor.waitForDataToLoad();
await this.chartEditor.setChartName(chartName);
await this.chartEditor.selectSource(sourceName);
if (seriesWhere != null) {
await this.chartEditor.setSeriesWhere(seriesWhere);
}
await this.chartEditor.runQuery(false);
await this.chartEditor.save();
await expect(this.getTiles()).toHaveCount(1, { timeout: 10000 });
Expand Down
Loading