@grafana/plugin-e2e 3.1.3 → 3.2.0-canary.2408.21202715809.0

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,25 @@
1
+ 'use strict';
2
+
3
+ const bootData = async ({ context }, use) => {
4
+ const tempPage = await context.newPage();
5
+ try {
6
+ await tempPage.goto("/");
7
+ const bootDataSettings = await tempPage.evaluate(() => {
8
+ return {
9
+ version: window.grafanaBootData.settings.buildInfo.version,
10
+ namespace: window.grafanaBootData.settings.namespace
11
+ };
12
+ });
13
+ await use({
14
+ version: bootDataSettings.version,
15
+ namespace: bootDataSettings.namespace
16
+ });
17
+ } catch (error) {
18
+ console.error("@grafana/plugin-e2e: Failed to fetch boot data", error);
19
+ await use({ version: void 0, namespace: void 0 });
20
+ } finally {
21
+ await tempPage.close();
22
+ }
23
+ };
24
+
25
+ exports.bootData = bootData;
@@ -1,12 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const grafanaVersion = async ({ page }, use) => {
4
- let grafanaVersion2 = process.env.GRAFANA_VERSION ?? "";
5
- if (!grafanaVersion2) {
6
- await page.goto("/");
7
- grafanaVersion2 = await page.evaluate("window.grafanaBootData.settings.buildInfo.version");
8
- }
9
- await use(grafanaVersion2.replace(/\-.*/, ""));
3
+ const grafanaVersion = async ({ bootData }, use) => {
4
+ const version = process.env.GRAFANA_VERSION || bootData.version || "";
5
+ await use(version.replace(/\-.*/, ""));
10
6
  };
11
7
 
12
8
  exports.grafanaVersion = grafanaVersion;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const namespace = async ({ bootData }, use) => {
4
+ await use(bootData.namespace || "default");
5
+ };
6
+
7
+ exports.namespace = namespace;
@@ -2,14 +2,96 @@
2
2
 
3
3
  var overrideGrafanaBootData = require('./scripts/overrideGrafanaBootData.js');
4
4
 
5
- const page = async ({ page: page2, featureToggles, userPreferences }, use) => {
6
- if (Object.keys(featureToggles).length > 0 || Object.keys(userPreferences).length > 0) {
5
+ function delay(ms) {
6
+ return new Promise((resolve) => setTimeout(resolve, ms));
7
+ }
8
+ async function handleBulkEvaluationRoute(route, featureToggles, latency) {
9
+ try {
10
+ const response = await route.fetch();
11
+ if (!response.ok()) {
12
+ await route.fulfill({ response });
13
+ return;
14
+ }
15
+ const body = await response.json();
16
+ for (const flag of body.flags) {
17
+ if (flag.key in featureToggles) {
18
+ flag.value = featureToggles[flag.key];
19
+ flag.reason = "STATIC";
20
+ flag.variant = "playwright-override";
21
+ }
22
+ }
23
+ for (const [key, value] of Object.entries(featureToggles)) {
24
+ const exists = body.flags.some((f) => f.key === key);
25
+ if (!exists) {
26
+ body.flags.push({
27
+ key,
28
+ value,
29
+ reason: "STATIC",
30
+ variant: "playwright-override"
31
+ });
32
+ }
33
+ }
34
+ if (latency > 0) {
35
+ await delay(latency);
36
+ }
37
+ await route.fulfill({
38
+ response,
39
+ body: JSON.stringify(body),
40
+ headers: { "content-type": "application/json" }
41
+ });
42
+ } catch (error) {
43
+ console.error("@grafana/plugin-e2e: Failed to intercept OFREP bulk evaluation", error);
44
+ await route.continue();
45
+ }
46
+ }
47
+ async function handleSingleFlagRoute(route, featureToggles, latency) {
48
+ try {
49
+ const url = new URL(route.request().url());
50
+ const flagKey = url.pathname.split("/").pop();
51
+ if (flagKey && flagKey in featureToggles) {
52
+ if (latency > 0) {
53
+ await delay(latency);
54
+ }
55
+ await route.fulfill({
56
+ status: 200,
57
+ body: JSON.stringify({
58
+ key: flagKey,
59
+ value: featureToggles[flagKey],
60
+ reason: "STATIC",
61
+ variant: "playwright-override"
62
+ }),
63
+ headers: { "content-type": "application/json" }
64
+ });
65
+ return;
66
+ }
67
+ await route.continue();
68
+ } catch (error) {
69
+ console.error("@grafana/plugin-e2e: Failed to intercept OFREP single flag evaluation", error);
70
+ await route.continue();
71
+ }
72
+ }
73
+ async function setupOpenFeatureRoutes(page2, featureToggles, latency, selectors) {
74
+ console.log("@grafana/plugin-e2e: setting up OpenFeature OFREP interception", { featureToggles, latency });
75
+ await page2.route(selectors.apis.OpenFeature.ofrepBulkPattern, async (route) => {
76
+ await handleBulkEvaluationRoute(route, featureToggles, latency);
77
+ });
78
+ await page2.route(selectors.apis.OpenFeature.ofrepSinglePattern, async (route) => {
79
+ await handleSingleFlagRoute(route, featureToggles, latency);
80
+ });
81
+ }
82
+ const page = async ({ page: page2, featureToggles, userPreferences, openFeatureLatency, selectors }, use) => {
83
+ const hasFeatureToggles = Object.keys(featureToggles).length > 0;
84
+ const hasUserPreferences = Object.keys(userPreferences).length > 0;
85
+ if (hasFeatureToggles || hasUserPreferences) {
7
86
  try {
8
87
  await page2.addInitScript(overrideGrafanaBootData.overrideGrafanaBootData, { featureToggles, userPreferences });
9
88
  } catch (error) {
10
89
  console.error("Failed to set feature toggles", error);
11
90
  }
12
91
  }
92
+ if (hasFeatureToggles) {
93
+ await setupOpenFeatureRoutes(page2, featureToggles, openFeatureLatency, selectors);
94
+ }
13
95
  await page2.goto("/");
14
96
  await use(page2);
15
97
  };
package/dist/index.d.ts CHANGED
@@ -521,6 +521,20 @@ declare const versionedAPIs: {
521
521
  "8.0.0": (pluginId: string) => string;
522
522
  };
523
523
  };
524
+ OpenFeature: {
525
+ ofrepBulkPattern: {
526
+ '12.1.0': string;
527
+ };
528
+ ofrepSinglePattern: {
529
+ '12.1.0': string;
530
+ };
531
+ ofrepBulkPath: {
532
+ '12.1.0': (namespace?: string) => string;
533
+ };
534
+ ofrepSinglePath: {
535
+ '12.1.0': (namespace?: string) => string;
536
+ };
537
+ };
524
538
  };
525
539
  type VersionedAPIs = typeof versionedAPIs;
526
540
 
@@ -563,25 +577,31 @@ type PluginOptions = {
563
577
  provisioningRootDir: string;
564
578
  /**
565
579
  * Optionally, you can add or override feature toggles.
566
- * The feature toggles you specify here will only work in the frontend. If you need a feature toggle to work across the entire stack, you
567
- * need to need to enable the feature in the Grafana config. Also see https://grafana.com/developers/plugin-tools/e2e-test-a-plugin/feature-toggles
580
+ * The feature toggles you specify here will work for both legacy feature toggles (via `window.grafanaBootData.settings.featureToggles`)
581
+ * and OpenFeature flags (via OFREP API interception).
582
+ *
583
+ * If you need a feature toggle to work across the entire stack, you need to enable the feature in the Grafana config.
584
+ * Also see https://grafana.com/developers/plugin-tools/e2e-test-a-plugin/feature-toggles
568
585
  *
569
- * To override feature toggles globally in the playwright.config.ts file:
586
+ * @example
587
+ * ```typescript
588
+ * // Override feature toggles globally in playwright.config.ts
570
589
  * export default defineConfig({
571
- use: {
572
- featureToggles: {
573
- exploreMixedDatasource: true,
574
- redshiftAsyncQueryDataSupport: false
575
- },
576
- },
577
- });
590
+ * use: {
591
+ * featureToggles: {
592
+ * exploreMixedDatasource: true,
593
+ * redshiftAsyncQueryDataSupport: false
594
+ * },
595
+ * },
596
+ * });
578
597
  *
579
- * To override feature toggles for tests in a certain file:
580
- test.use({
581
- featureToggles: {
582
- exploreMixedDatasource: true,
583
- },
598
+ * // Override feature toggles for tests in a specific file
599
+ * test.use({
600
+ * featureToggles: {
601
+ * exploreMixedDatasource: true,
602
+ * },
584
603
  * });
604
+ * ```
585
605
  */
586
606
  featureToggles: Record<string, boolean>;
587
607
  /**
@@ -619,6 +639,20 @@ type PluginOptions = {
619
639
  * If no credentials are provided, the server default admin:admin credentials will be used.
620
640
  */
621
641
  grafanaAPICredentials: Credentials;
642
+ /**
643
+ * Artificial latency in milliseconds to add to OpenFeature OFREP API responses.
644
+ * Useful for testing how the UI behaves with slow network conditions when fetching feature flags.
645
+ *
646
+ * @default 0
647
+ * @example
648
+ * ```typescript
649
+ * // Simulate 500ms network latency for OpenFeature flag fetching
650
+ * test.use({
651
+ * openFeatureLatency: 500,
652
+ * });
653
+ * ```
654
+ */
655
+ openFeatureLatency: number;
622
656
  };
623
657
  type PluginFixture = {
624
658
  /**
@@ -628,6 +662,13 @@ type PluginFixture = {
628
662
  * the version will be picked from window.grafanaBootData.settings.buildInfo.version.
629
663
  */
630
664
  grafanaVersion: string;
665
+ /**
666
+ * The Grafana namespace/tenant id that was detected when the test runner was started.
667
+ *
668
+ * The namespace will be picked from window.grafanaBootData.settings.namespace.
669
+ * Defaults to 'default' if not available.
670
+ */
671
+ namespace: string;
631
672
  /**
632
673
  * The E2E selectors to use for the current version of Grafana.
633
674
  * See https://grafana.com/developers/plugin-tools/e2e-test-a-plugin/selecting-elements#grafana-end-to-end-selectors for more information.
@@ -842,10 +883,15 @@ type PluginTestCtx = {
842
883
  selectors: E2ESelectorGroups;
843
884
  testInfo: TestInfo;
844
885
  } & Pick<PlaywrightTestArgs, 'page' | 'request'>;
886
+ /**
887
+ * Internal fixtures that are not exposed in the test API.
888
+ * These fixtures can be used by other fixtures but not by tests directly.
889
+ */
890
+ type InternalFixtures = {};
845
891
  /**
846
892
  * Playwright args used when defining fixtures
847
893
  */
848
- type PlaywrightArgs = PluginFixture & PluginOptions & PlaywrightTestArgs & PlaywrightTestOptions & PlaywrightWorkerArgs & PlaywrightWorkerOptions;
894
+ type PlaywrightArgs = PluginFixture & PluginOptions & InternalFixtures & PlaywrightTestArgs & PlaywrightTestOptions & PlaywrightWorkerArgs & PlaywrightWorkerOptions;
849
895
  /**
850
896
  * The data source settings
851
897
  */
@@ -1203,7 +1249,7 @@ declare class AnnotationPage extends GrafanaPage {
1203
1249
  clickAddNew(): Promise<AnnotationEditPage>;
1204
1250
  }
1205
1251
 
1206
- declare const test: _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & PluginFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & PluginOptions>;
1252
+ declare const test: _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & InternalFixtures & PluginFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & PluginOptions>;
1207
1253
  declare const expect: _playwright_test.Expect<{
1208
1254
  toHaveAlert: (grafanaPage: GrafanaPage, severity: AlertVariant, options?: AlertPageOptions) => Promise<{
1209
1255
  message: () => any;
@@ -1239,6 +1285,10 @@ declare global {
1239
1285
  grafanaBootData: {
1240
1286
  settings: {
1241
1287
  featureToggles: Record<string, boolean>;
1288
+ buildInfo: {
1289
+ version: string;
1290
+ };
1291
+ namespace: string;
1242
1292
  };
1243
1293
  };
1244
1294
  }
@@ -1288,4 +1338,4 @@ declare global {
1288
1338
  }
1289
1339
 
1290
1340
  export { AnnotationEditPage, AnnotationPage, AppConfigPage, AppPage, DashboardPage, DataSourceConfigPage, DataSourcePicker, ExplorePage, GrafanaPage, Panel, PanelEditPage, PluginConfigPage, TimeRange, VariableEditPage, VariablePage, expect, test };
1291
- export type { AlertPageOptions, AlertRule, AlertRuleArgs, AlertVariant$1 as AlertVariant, AppPageNavigateOptions, ContainTextOptions, CreateDataSourceArgs, CreateDataSourcePageArgs, Credentials, Dashboard, DashboardEditViewArgs, DashboardPageArgs, DataSourceSettings, E2ESelectorGroups, GotoAppConfigPageArgs, GotoAppPageArgs, GrafanaPageArgs, NavigateOptions, OrgRole, PlaywrightArgs, PluginFixture, PluginOptions, PluginPageArgs, PluginTestCtx, ReadProvisionedAlertRuleArgs, ReadProvisionedDashboardArgs, ReadProvisionedDataSourceArgs, RequestOptions, TimeRangeArgs, TriggerRequestOptions, User, UserPreferences, Visualization, getByGrafanaSelectorOptions };
1341
+ export type { AlertPageOptions, AlertRule, AlertRuleArgs, AlertVariant$1 as AlertVariant, AppPageNavigateOptions, ContainTextOptions, CreateDataSourceArgs, CreateDataSourcePageArgs, Credentials, Dashboard, DashboardEditViewArgs, DashboardPageArgs, DataSourceSettings, E2ESelectorGroups, GotoAppConfigPageArgs, GotoAppPageArgs, GrafanaPageArgs, InternalFixtures, NavigateOptions, OrgRole, PlaywrightArgs, PluginFixture, PluginOptions, PluginPageArgs, PluginTestCtx, ReadProvisionedAlertRuleArgs, ReadProvisionedDashboardArgs, ReadProvisionedDataSourceArgs, RequestOptions, TimeRangeArgs, TriggerRequestOptions, User, UserPreferences, Visualization, getByGrafanaSelectorOptions };
package/dist/index.js CHANGED
@@ -20,7 +20,9 @@ var readProvisionedDataSource = require('./fixtures/commands/readProvisionedData
20
20
  var readProvisionedAlertRule = require('./fixtures/commands/readProvisionedAlertRule.js');
21
21
  var dashboardPage = require('./fixtures/dashboardPage.js');
22
22
  var explorePage = require('./fixtures/explorePage.js');
23
+ var bootData = require('./fixtures/bootData.js');
23
24
  var grafanaVersion = require('./fixtures/grafanaVersion.js');
25
+ var namespace = require('./fixtures/namespace.js');
24
26
  var isFeatureToggleEnabled = require('./fixtures/isFeatureToggleEnabled.js');
25
27
  var page = require('./fixtures/page.js');
26
28
  var panelEditPage = require('./fixtures/panelEditPage.js');
@@ -53,9 +55,13 @@ var AppConfigPage = require('./models/pages/AppConfigPage.js');
53
55
  var PluginConfigPage = require('./models/pages/PluginConfigPage.js');
54
56
  var AppPage = require('./models/pages/AppPage.js');
55
57
 
56
- const test = test$1.test.extend({
58
+ const testWithInternal = test$1.test.extend({
59
+ bootData: bootData.bootData
60
+ });
61
+ const test = testWithInternal.extend({
57
62
  selectors: selectors.selectors,
58
63
  grafanaVersion: grafanaVersion.grafanaVersion,
64
+ namespace: namespace.namespace,
59
65
  login: login.login,
60
66
  grafanaAPIClient: grafanaAPIClient.grafanaAPIClient,
61
67
  createDataSourceConfigPage: createDataSourceConfigPage.createDataSourceConfigPage,
package/dist/options.js CHANGED
@@ -8,6 +8,7 @@ const DEFAULT_ADMIN_USER = {
8
8
  const options = {
9
9
  userPreferences: [{}, { option: true, scope: "worker" }],
10
10
  featureToggles: [{}, { option: true, scope: "worker" }],
11
+ openFeatureLatency: [0, { option: true, scope: "worker" }],
11
12
  provisioningRootDir: [path.join(process.cwd(), "provisioning"), { option: true, scope: "worker" }],
12
13
  user: [DEFAULT_ADMIN_USER, { option: true, scope: "worker" }],
13
14
  grafanaAPICredentials: [DEFAULT_ADMIN_USER, { option: true, scope: "worker" }]
@@ -43,6 +43,20 @@ const versionedAPIs = {
43
43
  settings: {
44
44
  [minGrafanaVersion.MIN_GRAFANA_VERSION]: (pluginId) => `/api/plugins/${pluginId}/settings`
45
45
  }
46
+ },
47
+ OpenFeature: {
48
+ ofrepBulkPattern: {
49
+ "12.1.0": "**/apis/features.grafana.app/**/ofrep/v*/evaluate/flags"
50
+ },
51
+ ofrepSinglePattern: {
52
+ "12.1.0": "**/apis/features.grafana.app/**/ofrep/v*/evaluate/flags/*"
53
+ },
54
+ ofrepBulkPath: {
55
+ "12.1.0": (namespace = "default") => `/apis/features.grafana.app/v0alpha1/namespaces/${namespace}/ofrep/v1/evaluate/flags`
56
+ },
57
+ ofrepSinglePath: {
58
+ "12.1.0": (namespace = "default") => `/apis/features.grafana.app/v0alpha1/namespaces/${namespace}/ofrep/v1/evaluate/flags`
59
+ }
46
60
  }
47
61
  };
48
62
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafana/plugin-e2e",
3
- "version": "3.1.3",
3
+ "version": "3.2.0-canary.2408.21202715809.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -47,5 +47,5 @@
47
47
  "uuid": "^13.0.0",
48
48
  "yaml": "^2.3.4"
49
49
  },
50
- "gitHead": "a9c6fab9844ef8b56a11c4ce4f72fdf6da9baea1"
50
+ "gitHead": "043c6c46215cc086eb03bedf1ea92a11cd94d2dc"
51
51
  }