diff --git a/e2e/custom-plugins/frontend-sandbox-app-test/img/logo.svg b/e2e/custom-plugins/frontend-sandbox-app-test/img/logo.svg
new file mode 100644
index 00000000000..08ec9068d45
--- /dev/null
+++ b/e2e/custom-plugins/frontend-sandbox-app-test/img/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/e2e/custom-plugins/frontend-sandbox-app-test/module.js b/e2e/custom-plugins/frontend-sandbox-app-test/module.js
new file mode 100644
index 00000000000..478549ee25a
--- /dev/null
+++ b/e2e/custom-plugins/frontend-sandbox-app-test/module.js
@@ -0,0 +1,41 @@
+/*
+ * This is a dummy plugin to test the frontend sandbox
+ * It is not meant to be used in any other way
+ * This file doesn't require any compilation
+ */
+define(['react', '@grafana/data', 'react-router-dom'], function (React, grafanaData, ReactRouterDom) {
+ const { AppPlugin } = grafanaData;
+ const { Switch, Route } = ReactRouterDom;
+
+ function PageOne() {
+ return React.createElement(
+ 'div',
+ {
+ 'data-testid': 'sandbox-app-test-page-one',
+ },
+ 'This is a page one'
+ );
+ }
+
+ function App() {
+ return React.createElement(Switch, null, React.createElement(Route, { component: PageOne }));
+ }
+ function AppConfig() {
+ return React.createElement(
+ 'div',
+ {
+ 'data-testid': 'sandbox-app-test-config-page',
+ },
+
+ 'This is a config page'
+ );
+ }
+
+ const plugin = new AppPlugin().setRootPage(App).addConfigPage({
+ title: 'Configuration',
+ icon: 'cog',
+ body: AppConfig,
+ id: 'configuration',
+ });
+ return { plugin };
+});
diff --git a/e2e/custom-plugins/frontend-sandbox-app-test/plugin.json b/e2e/custom-plugins/frontend-sandbox-app-test/plugin.json
new file mode 100644
index 00000000000..4997ee82633
--- /dev/null
+++ b/e2e/custom-plugins/frontend-sandbox-app-test/plugin.json
@@ -0,0 +1,35 @@
+{
+ "$schema": "https://raw.githubusercontent.com/grafana/grafana/master/docs/sources/developers/plugins/plugin.schema.json",
+ "type": "app",
+ "name": "Sandbox app test plugin",
+ "id": "sandbox-app-test",
+ "info": {
+ "keywords": ["app", "sandbox"],
+ "description": "",
+ "author": {
+ "name": "Grafana"
+ },
+ "logos": {
+ "small": "img/logo.svg",
+ "large": "img/logo.svg"
+ },
+ "links": [],
+ "screenshots": [],
+ "version": "1.0.0",
+ "updated": "2023-06-27"
+ },
+ "includes": [
+ {
+ "type": "page",
+ "icon": "cog",
+ "name": "Sandbox App Page",
+ "path": "/plugins/sandbox-app-test",
+ "role": "Admin",
+ "addToNav": true
+ }
+ ],
+ "dependencies": {
+ "grafanaDependency": ">=10.0",
+ "plugins": []
+ }
+}
diff --git a/e2e/custom-plugins/frontend-sandbox-panel-test/img/logo.svg b/e2e/custom-plugins/frontend-sandbox-panel-test/img/logo.svg
new file mode 100644
index 00000000000..08ec9068d45
--- /dev/null
+++ b/e2e/custom-plugins/frontend-sandbox-panel-test/img/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/e2e/panels-suite/frontend-sandbox-panel.spec.ts b/e2e/panels-suite/frontend-sandbox-panel.spec.ts
index 69205f06e09..fb237f6f54e 100644
--- a/e2e/panels-suite/frontend-sandbox-panel.spec.ts
+++ b/e2e/panels-suite/frontend-sandbox-panel.spec.ts
@@ -57,7 +57,7 @@ describe('Panel sandbox', () => {
});
cy.get('[data-testid="panel-editor-custom-editor-input"]').should('not.be.disabled');
- cy.get('[data-testid="panel-editor-custom-editor-input"]').type('x');
+ cy.get('[data-testid="panel-editor-custom-editor-input"]').type('x', { force: true });
cy.get('[data-sandbox-test="panel-editor"]').should('exist');
});
});
@@ -114,7 +114,7 @@ describe('Panel sandbox', () => {
});
cy.get('[data-testid="panel-editor-custom-editor-input"]').should('not.be.disabled');
- cy.get('[data-testid="panel-editor-custom-editor-input"]').type('x');
+ cy.get('[data-testid="panel-editor-custom-editor-input"]').type('x', { force: true });
cy.wait(100); // small delay to prevent false positives from too fast tests
cy.get('[data-sandbox-test="panel-editor"]').should('not.exist');
});
diff --git a/e2e/various-suite/frontend-sandbox-app.spec.ts b/e2e/various-suite/frontend-sandbox-app.spec.ts
new file mode 100644
index 00000000000..9bcf4d49832
--- /dev/null
+++ b/e2e/various-suite/frontend-sandbox-app.spec.ts
@@ -0,0 +1,85 @@
+import { e2e } from '../utils';
+
+const APP_ID = 'sandbox-app-test';
+
+describe('Datasource sandbox', () => {
+ before(() => {
+ e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), true);
+ cy.request({
+ url: `${Cypress.env('BASE_URL')}/api/plugins/${APP_ID}/settings`,
+ method: 'POST',
+ body: {
+ enabled: true,
+ },
+ });
+ });
+ beforeEach(() => {
+ e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), true);
+ });
+
+ describe('App Page', () => {
+ describe('Sandbox disabled', () => {
+ beforeEach(() => {
+ cy.window().then((win) => {
+ win.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=0');
+ });
+ });
+
+ it('Loads the app page without the sandbox div wrapper', () => {
+ e2e.pages.Home.visit();
+ e2e.components.NavBar.Toggle.button().click();
+ e2e.components.NavToolbar.container().get('[aria-label="Expand section Apps"]').click();
+ e2e.components.NavMenu.item().contains('Sandbox app test plugin').click();
+ cy.wait(200); // wait to prevent false positives because cypress checks too fast
+ cy.get('div[data-plugin-sandbox="sandbox-app-test"]').should('not.exist');
+ cy.get('div[data-testid="sandbox-app-test-page-one"]').should('exist');
+ });
+
+ it('Loads the app configuration without the sandbox div wrapper', () => {
+ e2e.pages.Home.visit();
+ e2e.components.NavBar.Toggle.button().click();
+ e2e.components.NavToolbar.container().get('[aria-label="Expand section Apps"]').click();
+ e2e.components.NavMenu.item().contains('Apps').click();
+ cy.get('a[aria-label="Tab Sandbox App Page"]').click();
+ cy.wait(200); // wait to prevent false positives because cypress checks too fast
+ cy.get('div[data-plugin-sandbox="sandbox-app-test"]').should('not.exist');
+ cy.get('div[data-testid="sandbox-app-test-config-page"]').should('exist');
+ });
+ });
+
+ describe('Sandbox enabled', () => {
+ beforeEach(() => {
+ cy.window().then((win) => {
+ win.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=1');
+ });
+ });
+
+ it('Loads the app page with the sandbox div wrapper', () => {
+ e2e.pages.Home.visit();
+ e2e.components.NavBar.Toggle.button().click();
+ e2e.components.NavToolbar.container().get('[aria-label="Expand section Apps"]').click();
+ e2e.components.NavMenu.item().contains('Sandbox app test plugin').click();
+ cy.get('div[data-plugin-sandbox="sandbox-app-test"]').should('exist');
+ cy.get('div[data-testid="sandbox-app-test-page-one"]').should('exist');
+ });
+
+ it('Loads the app configuration with the sandbox div wrapper', () => {
+ e2e.pages.Home.visit();
+ e2e.components.NavBar.Toggle.button().click();
+ e2e.components.NavToolbar.container().get('[aria-label="Expand section Apps"]').click();
+ e2e.components.NavMenu.item().contains('Apps').click();
+ cy.get('a[aria-label="Tab Sandbox App Page"]').click();
+ cy.get('div[data-plugin-sandbox="sandbox-app-test"]').should('exist');
+ cy.get('div[data-testid="sandbox-app-test-config-page"]').should('exist');
+ });
+ });
+ });
+
+ afterEach(() => {
+ e2e.flows.revertAllChanges();
+ });
+
+ after(() => {
+ cy.clearCookies();
+ });
+});