mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Implement registrator for preference items
Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
c581713ca1
commit
ad1d9427fe
@ -7,6 +7,8 @@ import type { IObservableValue } from "mobx";
|
||||
import { runInAction, computed, observable } from "mobx";
|
||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||
import { getSingleElement, queryAllElements, querySingleElement } from "../../renderer/components/test-utils/discovery-of-html-elements";
|
||||
import React from "react";
|
||||
|
||||
describe("preferences: extension adding preference tabs", () => {
|
||||
let builder: ApplicationBuilder;
|
||||
@ -49,11 +51,43 @@ describe("preferences: extension adding preference tabs", () => {
|
||||
visible: computed(() => someObservable.get()),
|
||||
},
|
||||
],
|
||||
|
||||
appPreferences: [
|
||||
{
|
||||
title: "some-title",
|
||||
id: "some-preference-item-id",
|
||||
showInPreferencesTab: "some-preference-tab-id",
|
||||
|
||||
components: {
|
||||
Hint: () => <div />,
|
||||
Input: () => <div />,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "some-other-title",
|
||||
id: "some-other-preference-item-id",
|
||||
showInPreferencesTab: "some-other-preference-tab-id",
|
||||
|
||||
components: {
|
||||
Hint: () => <div />,
|
||||
Input: () => <div />,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "some-another-title",
|
||||
id: "some-another-preference-item-id",
|
||||
showInPreferencesTab: "some-preference-tab-id-with-controlled-visibility",
|
||||
|
||||
components: {
|
||||
Hint: () => <div />,
|
||||
Input: () => <div />,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
builder.extensions.enable(testExtension);
|
||||
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
@ -61,20 +95,25 @@ describe("preferences: extension adding preference tabs", () => {
|
||||
});
|
||||
|
||||
it("shows tabs in order", () => {
|
||||
const actual = rendered.queryAllByTestId(/tab-link-for-extension-some-extension-nav-item-(.*)/).map(x => x.dataset.testid);
|
||||
const actual = queryAllElements("preference-tab-link")(
|
||||
rendered,
|
||||
).attributeValues.filter((value) =>
|
||||
value?.startsWith("extension-some-extension"),
|
||||
);
|
||||
|
||||
expect(actual).toEqual([
|
||||
"tab-link-for-extension-some-extension-nav-item-some-other-preference-tab-id",
|
||||
"tab-link-for-extension-some-extension-nav-item-some-preference-tab-id",
|
||||
"extension-some-extension-some-other-preference-tab-id",
|
||||
"extension-some-extension-some-preference-tab-id",
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not show hidden tab", () => {
|
||||
const actual = rendered.queryByTestId(
|
||||
"tab-link-for-extension-some-extension-nav-item-some-preference-tab-id-with-controlled-visibility",
|
||||
);
|
||||
const actual = querySingleElement(
|
||||
"preference-tab-link",
|
||||
"extension-some-extension-some-preference-tab-id-with-controlled-visibility",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).not.toBeInTheDocument();
|
||||
expect(actual).toBeNull();
|
||||
});
|
||||
|
||||
it("when item becomes visible, shows the tab", () => {
|
||||
@ -82,11 +121,12 @@ describe("preferences: extension adding preference tabs", () => {
|
||||
someObservable.set(true);
|
||||
});
|
||||
|
||||
const actual = rendered.queryByTestId(
|
||||
"tab-link-for-extension-some-extension-nav-item-some-preference-tab-id-with-controlled-visibility",
|
||||
);
|
||||
const actual = getSingleElement(
|
||||
"preference-tab-link",
|
||||
"extension-some-extension-some-preference-tab-id-with-controlled-visibility",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).toBeInTheDocument();
|
||||
expect(actual).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -7,8 +7,9 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||
import React from "react";
|
||||
import "@testing-library/jest-dom/extend-expect";
|
||||
import extensionPreferencesRouteInjectable from "../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
|
||||
import type { FakeExtensionOptions } from "../../renderer/components/test-utils/get-extension-fake";
|
||||
import { getSingleElement, queryAllElements, querySingleElement } from "../../renderer/components/test-utils/discovery-of-html-elements";
|
||||
import logErrorInjectable from "../../common/log-error.injectable";
|
||||
|
||||
describe("preferences - navigation to extension specific preferences", () => {
|
||||
let builder: ApplicationBuilder;
|
||||
@ -19,9 +20,14 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
|
||||
describe("given in preferences, when rendered", () => {
|
||||
let rendered: RenderResult;
|
||||
let logErrorMock: jest.Mock;
|
||||
|
||||
beforeEach(async () => {
|
||||
builder.beforeWindowStart(() => {
|
||||
logErrorMock = jest.fn();
|
||||
|
||||
builder.beforeWindowStart((windowDi) => {
|
||||
windowDi.override(logErrorInjectable, () => logErrorMock);
|
||||
|
||||
builder.preferences.navigate();
|
||||
});
|
||||
|
||||
@ -33,7 +39,8 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
});
|
||||
|
||||
it("does not show extension preferences yet", () => {
|
||||
const page = rendered.queryByTestId("extension-preferences-page");
|
||||
// Todo: check if query is correct.
|
||||
const page = querySingleElement("preference-page", "extension")(rendered);
|
||||
|
||||
expect(page).toBeNull();
|
||||
});
|
||||
@ -52,7 +59,7 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
);
|
||||
|
||||
builder.preferences.navigation.click(
|
||||
"extension-some-test-extension-id",
|
||||
"some-test-extension-id",
|
||||
);
|
||||
});
|
||||
|
||||
@ -61,13 +68,19 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
});
|
||||
|
||||
it("doesn't show preferences from unrelated extension", () => {
|
||||
const actual = rendered.queryByTestId("extension-preference-item-for-some-other-preference-item-id");
|
||||
const actual = querySingleElement(
|
||||
"preference-page",
|
||||
"preference-item-for-extension-some-other-test-extension-id-page",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).toBeNull();
|
||||
});
|
||||
|
||||
it("shows preferences from related extension", () => {
|
||||
const actual = rendered.getByTestId("extension-preference-item-for-some-preference-item-id");
|
||||
const actual = getSingleElement(
|
||||
"preference-page",
|
||||
"preference-item-for-extension-some-test-extension-id-page",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).not.toBeNull();
|
||||
});
|
||||
@ -104,21 +117,18 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
expect(rendered.container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows link for extension preferences", () => {
|
||||
const actual = rendered.getByTestId("tab-link-for-extension-some-test-extension-id");
|
||||
|
||||
expect(actual).not.toBeNull();
|
||||
});
|
||||
|
||||
it("link should not be active", () => {
|
||||
const actual = rendered.getByTestId("tab-link-for-extension-some-test-extension-id");
|
||||
const actual = getSingleElement(
|
||||
"preference-tab-link",
|
||||
"some-test-extension-id",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).not.toHaveClass("active");
|
||||
});
|
||||
|
||||
describe("when navigating to extension preferences using navigation", () => {
|
||||
beforeEach(() => {
|
||||
builder.preferences.navigation.click("extension-some-test-extension-id");
|
||||
builder.preferences.navigation.click("some-test-extension-id");
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
@ -126,31 +136,23 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
});
|
||||
|
||||
it("shows proper page title", () => {
|
||||
const title = rendered.getByText("some-test-extension-id preferences");
|
||||
const title = getSingleElement("preference-page-title")(rendered);
|
||||
|
||||
expect(title).toBeInTheDocument();
|
||||
expect(title).toHaveTextContent("some-test-extension-id preferences");
|
||||
});
|
||||
|
||||
it("shows extension specific preferences", () => {
|
||||
const page = rendered.getByTestId("extension-preferences-page");
|
||||
it("shows only extension specific preference items", () => {
|
||||
const actual = queryAllElements(
|
||||
"preference-item",
|
||||
)(rendered).attributeValues;
|
||||
|
||||
expect(page).not.toBeNull();
|
||||
});
|
||||
|
||||
it("shows extension specific preference item", () => {
|
||||
const actual = rendered.getByTestId("extension-preference-item-for-some-preference-item-id");
|
||||
|
||||
expect(actual).not.toBeNull();
|
||||
});
|
||||
|
||||
it("does not show unrelated preference tab items", () => {
|
||||
const actual = rendered.queryByTestId("extension-preference-item-for-some-unrelated-preference-item-id");
|
||||
|
||||
expect(actual).toBeNull();
|
||||
expect(actual).toEqual([
|
||||
"preference-item-for-extension-some-test-extension-id-item-some-preference-item-id",
|
||||
]);
|
||||
});
|
||||
|
||||
it("link is active", () => {
|
||||
const actual = rendered.getByTestId("tab-link-for-extension-some-test-extension-id");
|
||||
const actual = getSingleElement("preference-tab-link", "some-test-extension-id")(rendered);
|
||||
|
||||
expect(actual).toHaveClass("active");
|
||||
});
|
||||
@ -164,14 +166,21 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows the error message about extension not being present", () => {
|
||||
expect(rendered.getByTestId("error-for-extension-not-being-present")).toBeInTheDocument();
|
||||
it("does not show any preference page", () => {
|
||||
const actual = querySingleElement("preference-page")(rendered);
|
||||
|
||||
expect(actual).toBeNull();
|
||||
});
|
||||
|
||||
it("when extension is enabled again, does not show the error message anymore", () => {
|
||||
it("when extension is enabled again, shows the preference page", () => {
|
||||
builder.extensions.enable(extensionStubWithExtensionSpecificPreferenceItems);
|
||||
|
||||
expect(rendered.queryByTestId("error-for-extension-not-being-present")).not.toBeInTheDocument();
|
||||
const actual = getSingleElement(
|
||||
"preference-page",
|
||||
"preference-item-for-extension-some-test-extension-id-page",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -179,90 +188,104 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
|
||||
describe("given extension with registered tab", () => {
|
||||
beforeEach(() => {
|
||||
builder.extensions.enable(extensionStubWithWithRegisteredTab);
|
||||
builder.extensions.enable(extensionStubWithRegisteredTab);
|
||||
});
|
||||
|
||||
it("logs error", () => {
|
||||
expect(
|
||||
logErrorMock.mock.calls[0][0].startsWith(
|
||||
"Tried to create preferences, but encountered references to unknown ids",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows extension tab in general area", () => {
|
||||
const actual = rendered.getByTestId("tab-link-for-extension-registered-tab-page-id-nav-item-metrics-extension-tab");
|
||||
const generalTabGroup = getSingleElement(
|
||||
"preference-tab-group",
|
||||
"general-tab-group",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).toMatchSnapshot();
|
||||
const actual = getSingleElement(
|
||||
"preference-tab-link",
|
||||
"extension-registered-tab-page-id-metrics-extension-tab",
|
||||
)(generalTabGroup);
|
||||
|
||||
expect(actual).not.toBeNull();
|
||||
});
|
||||
|
||||
it("does not show custom settings block", () => {
|
||||
const actual = rendered.queryByTestId("extension-settings");
|
||||
it("does not show tab group for extensions for there being no content", () => {
|
||||
const actual = querySingleElement(
|
||||
"preference-tab-group",
|
||||
"extensions-tab-group",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).not.toBeInTheDocument();
|
||||
expect(actual).toBeNull();
|
||||
});
|
||||
|
||||
describe("when navigating to specific extension tab", () => {
|
||||
beforeEach(() => {
|
||||
builder.preferences.navigation.click("extension-registered-tab-page-id-nav-item-metrics-extension-tab");
|
||||
builder.preferences.navigation.click("extension-registered-tab-page-id-metrics-extension-tab");
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows related preferences for this tab", () => {
|
||||
const actual = rendered.getByTestId("metrics-preference-item-hint");
|
||||
|
||||
expect(actual).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show unrelated preferences for this tab", () => {
|
||||
const actual = rendered.queryByTestId("survey-preference-item-hint");
|
||||
|
||||
expect(actual).not.toBeInTheDocument();
|
||||
});
|
||||
it("shows page title", () => {
|
||||
const pageTitle = rendered.queryByTestId("extension-preferences-page-title");
|
||||
|
||||
expect(pageTitle).toBeInTheDocument();
|
||||
it("shows correct page title", () => {
|
||||
const pageTitle = getSingleElement("preference-page-title")(rendered);
|
||||
|
||||
expect(pageTitle).toHaveTextContent("Metrics tab");
|
||||
});
|
||||
it("shows page title same as tab title", () => {
|
||||
const pageTitle = rendered.queryByTestId("extension-preferences-page-title");
|
||||
const tabs = extensionStubWithWithRegisteredTab.rendererOptions?.appPreferenceTabs;
|
||||
const tabTitle = tabs && tabs[0].title;
|
||||
|
||||
expect(pageTitle?.innerHTML).toBe(tabTitle);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("given extension with few registered tabs", () => {
|
||||
const tabs = [
|
||||
"tab-link-for-extension-hello-world-tab-page-id-nav-item-hello-extension-tab",
|
||||
"tab-link-for-extension-hello-world-tab-page-id-nav-item-logs-extension-tab",
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
builder.extensions.enable(extensionStubWithWithRegisteredTabs);
|
||||
});
|
||||
|
||||
it.each(tabs)("shows '%s' tab in general area", (tab) => {
|
||||
const tabElement = rendered.getByTestId(tab);
|
||||
|
||||
expect(tabElement).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("given extensions with tabs having same id", () => {
|
||||
beforeEach(() => {
|
||||
builder.extensions.enable(extensionStubWithWithRegisteredTab, extensionStubWithWithSameRegisteredTab);
|
||||
builder.extensions.enable(
|
||||
extensionStubWithRegisteredTab,
|
||||
extensionStubWithSameRegisteredTab,
|
||||
);
|
||||
});
|
||||
|
||||
it("shows tab from the first extension", () => {
|
||||
const actual = rendered.getByTestId("tab-link-for-extension-registered-tab-page-id-nav-item-metrics-extension-tab");
|
||||
const actual = getSingleElement(
|
||||
"preference-tab-link",
|
||||
"extension-registered-tab-page-id-metrics-extension-tab",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows tab from the second extension", () => {
|
||||
const actual = rendered.getByTestId("tab-link-for-extension-duplicated-tab-page-id-nav-item-metrics-extension-tab");
|
||||
const actual = getSingleElement(
|
||||
"preference-tab-link",
|
||||
"extension-duplicated-tab-page-id-metrics-extension-tab",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("when navigating to first extension tab", () => {
|
||||
beforeEach(() => {
|
||||
builder.preferences.navigation.click("extension-registered-tab-page-id-nav-item-metrics-extension-tab");
|
||||
builder.preferences.navigation.click(
|
||||
"extension-registered-tab-page-id-metrics-extension-tab",
|
||||
);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
@ -270,21 +293,20 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
});
|
||||
|
||||
it("shows related preferences for this tab", () => {
|
||||
const actual = rendered.getByTestId("metrics-preference-item-hint");
|
||||
const actual =
|
||||
queryAllElements("preference-item")(rendered).attributeValues;
|
||||
|
||||
expect(actual).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show unrelated preferences for this tab", () => {
|
||||
const actual = rendered.queryByTestId("another-metrics-preference-item-hint");
|
||||
|
||||
expect(actual).not.toBeInTheDocument();
|
||||
expect(actual).toEqual([
|
||||
"preference-item-for-extension-registered-tab-page-id-item-metrics-preference-item-id",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when navigating to second extension tab", () => {
|
||||
beforeEach(() => {
|
||||
builder.preferences.navigation.click("extension-duplicated-tab-page-id-nav-item-metrics-extension-tab");
|
||||
builder.preferences.navigation.click(
|
||||
"extension-duplicated-tab-page-id-metrics-extension-tab",
|
||||
);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
@ -292,74 +314,16 @@ describe("preferences - navigation to extension specific preferences", () => {
|
||||
});
|
||||
|
||||
it("shows related preferences for this tab", () => {
|
||||
const actual = rendered.getByTestId("another-metrics-preference-item-hint");
|
||||
const actual =
|
||||
queryAllElements("preference-item")(rendered).attributeValues;
|
||||
|
||||
expect(actual).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show unrelated preferences for this tab", () => {
|
||||
const actual = rendered.queryByTestId("metrics-preference-item-hint");
|
||||
|
||||
expect(actual).not.toBeInTheDocument();
|
||||
expect(actual).toEqual([
|
||||
"preference-item-for-extension-duplicated-tab-page-id-item-another-metrics-preference-item-id",
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when navigating to extension specific tab", () => {
|
||||
let rendered: RenderResult;
|
||||
|
||||
beforeEach(async () => {
|
||||
builder.beforeWindowStart((windowDi) => {
|
||||
const extensionRoute = windowDi.inject(extensionPreferencesRouteInjectable);
|
||||
|
||||
const params = { parameters: {
|
||||
extensionId: "duplicated-tab-page-id",
|
||||
tabId: "metrics-extension-tab",
|
||||
}};
|
||||
|
||||
builder.preferences.navigateTo(extensionRoute, params);
|
||||
});
|
||||
|
||||
builder.extensions.enable(extensionStubWithWithSameRegisteredTab, extensionUsingSomeoneElseTab);
|
||||
rendered = await builder.render();
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("does render related preferences for specific tab", () => {
|
||||
expect(rendered.getByTestId("another-metrics-preference-item-hint")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render related preferences for specific tab", () => {
|
||||
expect(rendered.queryByTestId("my-preferences-item-hint")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when navigating to someone else extension specific tab", () => {
|
||||
let rendered: RenderResult;
|
||||
|
||||
beforeEach(async () => {
|
||||
builder.beforeWindowStart((windowDi) => {
|
||||
const extensionRoute = windowDi.inject(extensionPreferencesRouteInjectable);
|
||||
const params = { parameters: {
|
||||
extensionId: "extension-using-someone-else-tab-id",
|
||||
tabId: "metrics-extension-tab",
|
||||
}};
|
||||
|
||||
builder.preferences.navigateTo(extensionRoute, params);
|
||||
});
|
||||
|
||||
builder.extensions.enable(extensionStubWithWithSameRegisteredTab, extensionUsingSomeoneElseTab);
|
||||
rendered = await builder.render();
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const extensionStubWithExtensionSpecificPreferenceItems: FakeExtensionOptions = {
|
||||
@ -436,7 +400,7 @@ const extensionStubWithShowInPreferencesTab: FakeExtensionOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
const extensionStubWithWithRegisteredTab: FakeExtensionOptions = {
|
||||
const extensionStubWithRegisteredTab: FakeExtensionOptions = {
|
||||
id: "registered-tab-page-id",
|
||||
name: "registered-tab-page-id",
|
||||
|
||||
@ -482,47 +446,7 @@ const extensionStubWithWithRegisteredTab: FakeExtensionOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
const extensionStubWithWithRegisteredTabs: FakeExtensionOptions = {
|
||||
id: "hello-world-tab-page-id",
|
||||
name: "hello-world-tab-page-id",
|
||||
|
||||
rendererOptions: {
|
||||
appPreferences: [
|
||||
{
|
||||
title: "Hello world",
|
||||
id: "hello-preference-item-id",
|
||||
showInPreferencesTab: "hello-extension-tab",
|
||||
|
||||
components: {
|
||||
Hint: () => <div data-testid="hello-preference-item-hint" />,
|
||||
Input: () => <div data-testid="hello-preference-item-input" />,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Logs",
|
||||
id: "logs-preference-item-id",
|
||||
showInPreferencesTab: "logs-extension-tab",
|
||||
|
||||
components: {
|
||||
Hint: () => <div data-testid="logs-preference-item-hint" />,
|
||||
Input: () => <div data-testid="logs-preference-item-input" />,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
appPreferenceTabs: [{
|
||||
title: "Metrics tab",
|
||||
id: "hello-extension-tab",
|
||||
orderNumber: 100,
|
||||
}, {
|
||||
title: "Logs tab",
|
||||
id: "logs-extension-tab",
|
||||
orderNumber: 200,
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
const extensionStubWithWithSameRegisteredTab: FakeExtensionOptions = {
|
||||
const extensionStubWithSameRegisteredTab: FakeExtensionOptions = {
|
||||
id: "duplicated-tab-page-id",
|
||||
name: "duplicated-tab-page-id",
|
||||
|
||||
@ -547,23 +471,3 @@ const extensionStubWithWithSameRegisteredTab: FakeExtensionOptions = {
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
const extensionUsingSomeoneElseTab: FakeExtensionOptions = {
|
||||
id: "extension-using-someone-else-tab-id",
|
||||
name: "extension-using-someone-else-tab-id",
|
||||
|
||||
rendererOptions: {
|
||||
appPreferences: [
|
||||
{
|
||||
title: "My preferences",
|
||||
id: "my-preferences-item-id",
|
||||
showInPreferencesTab: "metrics-extension-tab",
|
||||
|
||||
components: {
|
||||
Hint: () => <div data-testid="my-preferences-item-hint" />,
|
||||
Input: () => <div data-testid="my-preferences-item-input" />,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@ -9,7 +9,7 @@ import { getApplicationBuilder } from "../../renderer/components/test-utils/get-
|
||||
import navigateToTelemetryPreferencesInjectable from "./common/navigate-to-telemetry-preferences.injectable";
|
||||
import sentryDataSourceNameInjectable from "../../common/vars/sentry-dsn-url.injectable";
|
||||
import type { FakeExtensionOptions } from "../../renderer/components/test-utils/get-extension-fake";
|
||||
import { getSingleElement, querySingleElement } from "../../renderer/components/test-utils/discovery-of-html-elements";
|
||||
import { getSingleElement, queryAllElements, querySingleElement } from "../../renderer/components/test-utils/discovery-of-html-elements";
|
||||
|
||||
describe("preferences - navigation to telemetry preferences", () => {
|
||||
let builder: ApplicationBuilder;
|
||||
@ -43,7 +43,10 @@ describe("preferences - navigation to telemetry preferences", () => {
|
||||
});
|
||||
|
||||
it("does not show link for telemetry preferences", () => {
|
||||
const actual = rendered.queryByTestId("tab-link-for-telemetry");
|
||||
const actual = querySingleElement(
|
||||
"preference-tab-link",
|
||||
"telemetry",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).toBeNull();
|
||||
});
|
||||
@ -60,7 +63,10 @@ describe("preferences - navigation to telemetry preferences", () => {
|
||||
});
|
||||
|
||||
it("shows link for telemetry preferences", () => {
|
||||
const actual = rendered.getByTestId("tab-link-for-telemetry");
|
||||
const actual = getSingleElement(
|
||||
"preference-tab-link",
|
||||
"telemetry",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).not.toBeNull();
|
||||
});
|
||||
@ -84,11 +90,12 @@ describe("preferences - navigation to telemetry preferences", () => {
|
||||
});
|
||||
|
||||
it("shows extension telemetry preference items", () => {
|
||||
const actual = rendered.getByTestId(
|
||||
"telemetry-preference-item-for-some-telemetry-preference-item-id",
|
||||
);
|
||||
const actual =
|
||||
queryAllElements("preference-item")(rendered).attributeValues;
|
||||
|
||||
expect(actual).not.toBeNull();
|
||||
expect(actual).toEqual([
|
||||
"preference-item-for-extension-some-test-extension-name-item-some-telemetry-preference-item-id",
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -110,7 +117,10 @@ describe("preferences - navigation to telemetry preferences", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const actual = rendered.queryByTestId("tab-link-for-telemetry");
|
||||
const actual = querySingleElement(
|
||||
"preference-tab-link",
|
||||
"telemetry",
|
||||
)(rendered);
|
||||
|
||||
expect(actual).toBeNull();
|
||||
});
|
||||
@ -139,9 +149,11 @@ describe("preferences - navigation to telemetry preferences", () => {
|
||||
});
|
||||
|
||||
it("allows configuration of automatic error reporting", () => {
|
||||
const actual = rendered.getByTestId("telemetry-preferences-for-automatic-error-reporting");
|
||||
const actual = queryAllElements(
|
||||
"preference-item",
|
||||
)(rendered).attributeValues;
|
||||
|
||||
expect(actual).not.toBeNull();
|
||||
expect(actual).toEqual(["automatic-error-reporting"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -168,9 +180,11 @@ describe("preferences - navigation to telemetry preferences", () => {
|
||||
});
|
||||
|
||||
it("does not allow configuration of automatic error reporting", () => {
|
||||
const actual = rendered.queryByTestId("telemetry-preferences-for-automatic-error-reporting");
|
||||
const actual = queryAllElements(
|
||||
"preference-item",
|
||||
)(rendered).attributeValues;
|
||||
|
||||
expect(actual).toBeNull();
|
||||
expect(actual).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { SubTitle } from "../../../../renderer/components/layout/sub-title";
|
||||
import type { AppPreferenceRegistration } from "../../../../renderer/components/+preferences/app-preferences/app-preference-registration";
|
||||
import React from "react";
|
||||
|
||||
export interface ExtensionSettingsProps {
|
||||
registration: AppPreferenceRegistration;
|
||||
}
|
||||
|
||||
export function ExtensionPreferenceItem({ registration }: ExtensionSettingsProps) {
|
||||
const {
|
||||
title,
|
||||
id,
|
||||
components: { Hint, Input },
|
||||
} = registration;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<section
|
||||
id={id}
|
||||
className="small"
|
||||
>
|
||||
<SubTitle title={title} />
|
||||
<Input />
|
||||
<div className="hint">
|
||||
<Hint />
|
||||
</div>
|
||||
</section>
|
||||
<hr className="small" />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension";
|
||||
import { preferenceItemInjectionToken } from "../preference-items/preference-item-injection-token";
|
||||
import { extensionRegistratorInjectionToken } from "../../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import { getPreferencePage } from "../get-preference-page";
|
||||
import { ExtensionPreferenceItem } from "./extension-preference-item";
|
||||
import { computed } from "mobx";
|
||||
|
||||
const registratorForPreferenceItemsInjectable = getInjectable({
|
||||
id: "registrator-for-preference-items",
|
||||
|
||||
instantiate: () => (ext) => {
|
||||
const extension = ext as LensRendererExtension;
|
||||
|
||||
const commonId = `preference-item-for-extension-${extension.sanitizedExtensionId}`;
|
||||
|
||||
const tabId = `${commonId}-primary-tab`;
|
||||
const primaryTabInjectable = getInjectable({
|
||||
id: tabId,
|
||||
|
||||
instantiate: () => ({
|
||||
kind: "tab" as const,
|
||||
id: tabId,
|
||||
parentId: "extensions-tab-group",
|
||||
pathId: extension.sanitizedExtensionId,
|
||||
label: extension.name,
|
||||
orderNumber: 10,
|
||||
}),
|
||||
|
||||
injectionToken: preferenceItemInjectionToken,
|
||||
});
|
||||
|
||||
const pageId = `${commonId}-page`;
|
||||
const primaryPageInjectable = getInjectable({
|
||||
id: pageId,
|
||||
|
||||
instantiate: () => ({
|
||||
kind: "page" as const,
|
||||
id: pageId,
|
||||
parentId: tabId,
|
||||
orderNumber: 0,
|
||||
Component: getPreferencePage(`${extension.name} preferences`),
|
||||
childrenSeparator: () => <hr />,
|
||||
}),
|
||||
|
||||
injectionToken: preferenceItemInjectionToken,
|
||||
});
|
||||
|
||||
const additionalTabs = extension.appPreferenceTabs.map(
|
||||
(registration) => {
|
||||
const additionalTabId = `${commonId}-additional-tab-${registration.id}`;
|
||||
|
||||
return getInjectable({
|
||||
id: additionalTabId,
|
||||
|
||||
instantiate: () => ({
|
||||
kind: "tab" as const,
|
||||
id: additionalTabId,
|
||||
parentId: "general-tab-group",
|
||||
pathId: `extension-${extension.sanitizedExtensionId}-${registration.id}`,
|
||||
label: registration.title,
|
||||
isShown: computed(() => registration.visible?.get() ?? true),
|
||||
orderNumber: registration.orderNumber || 100,
|
||||
}),
|
||||
|
||||
injectionToken: preferenceItemInjectionToken,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const additionalPages = extension.appPreferenceTabs.map(
|
||||
(registration) => {
|
||||
const additionalPageId = `${commonId}-additional-page-${registration.id}`;
|
||||
const additionalTabId = `${commonId}-additional-tab-${registration.id}`;
|
||||
|
||||
return getInjectable({
|
||||
id: additionalPageId,
|
||||
|
||||
instantiate: () => ({
|
||||
kind: "page" as const,
|
||||
id: additionalPageId,
|
||||
parentId: additionalTabId,
|
||||
Component: getPreferencePage(registration.title),
|
||||
}),
|
||||
|
||||
injectionToken: preferenceItemInjectionToken,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const items = extension.appPreferences.map((registration, i) => {
|
||||
const itemId = `${commonId}-item-${registration.id ?? i}`;
|
||||
|
||||
return getInjectable({
|
||||
id: itemId,
|
||||
|
||||
instantiate: () => ({
|
||||
kind: "item" as const,
|
||||
id: itemId,
|
||||
|
||||
// Note: Legacy extensions considered telemetry as magic string, and so does this code
|
||||
parentId: registration.showInPreferencesTab
|
||||
? registration.showInPreferencesTab === "telemetry"
|
||||
? "telemetry-page"
|
||||
: `${commonId}-additional-page-${registration.showInPreferencesTab}`
|
||||
: pageId,
|
||||
|
||||
orderNumber: i * 10,
|
||||
|
||||
Component: () => (
|
||||
<ExtensionPreferenceItem registration={registration} />
|
||||
),
|
||||
|
||||
childrenSeparator: () => <hr />,
|
||||
}),
|
||||
|
||||
injectionToken: preferenceItemInjectionToken,
|
||||
});
|
||||
});
|
||||
|
||||
return [
|
||||
primaryTabInjectable,
|
||||
...additionalTabs,
|
||||
primaryPageInjectable,
|
||||
...additionalPages,
|
||||
...items,
|
||||
];
|
||||
},
|
||||
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
export default registratorForPreferenceItemsInjectable;
|
||||
@ -7,7 +7,7 @@ import type { PreferencePageComponent } from "./preference-items/preference-item
|
||||
|
||||
export const getPreferencePage = (label: string): PreferencePageComponent => ({ children, item }) => (
|
||||
<section id={item.id} data-preference-page-test={item.id}>
|
||||
<h2>{label}</h2>
|
||||
<h2 data-preference-page-title-test={true}>{label}</h2>
|
||||
|
||||
{children}
|
||||
</section>
|
||||
|
||||
@ -11,7 +11,6 @@ import preferencesRouteInjectable from "../../common/preferences-route.injectabl
|
||||
import { filter, map } from "lodash/fp";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { normalizeComposite } from "../../../application-menu/main/menu-items/get-composite/normalize-composite/normalize-composite";
|
||||
import { findExactlyOne } from "../../../../common/utils/find-exactly-one/find-exactly-one";
|
||||
import preferencesCompositeInjectable from "./preferences-composite.injectable";
|
||||
import type { PreferenceTabsRoot } from "./preference-tab-root";
|
||||
|
||||
@ -26,12 +25,18 @@ const currentPreferenceTabCompositeInjectable = getInjectable({
|
||||
return computed(() => {
|
||||
const { preferenceTabId } = routePathParameters.get();
|
||||
|
||||
return pipeline(
|
||||
const tabComposites = pipeline(
|
||||
normalizeComposite(preferencesComposite.get()),
|
||||
map(([, composite]) => composite),
|
||||
filter(isPreferenceTab),
|
||||
findExactlyOne(hasMatchingPathId(preferenceTabId)),
|
||||
filter(hasMatchingPathId(preferenceTabId)),
|
||||
);
|
||||
|
||||
if (tabComposites.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return tabComposites[0];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import type React from "react";
|
||||
|
||||
export type PreferenceItemComponent = React.ComponentType<{
|
||||
@ -21,7 +22,7 @@ export interface PreferenceTab {
|
||||
pathId: string;
|
||||
label: string;
|
||||
orderNumber: number;
|
||||
isShown?: boolean;
|
||||
isShown?: IComputedValue<boolean> | boolean;
|
||||
}
|
||||
|
||||
export interface PreferenceTabGroup {
|
||||
@ -30,7 +31,7 @@ export interface PreferenceTabGroup {
|
||||
parentId: "preference-tabs";
|
||||
label: string;
|
||||
orderNumber: number;
|
||||
isShown?: boolean;
|
||||
isShown?: IComputedValue<boolean> | boolean;
|
||||
iconName?: string;
|
||||
}
|
||||
|
||||
@ -38,7 +39,7 @@ export interface PreferencePage {
|
||||
kind: "page";
|
||||
id: string;
|
||||
parentId: string;
|
||||
isShown?: boolean;
|
||||
isShown?: IComputedValue<boolean> | boolean;
|
||||
childrenSeparator?: () => React.ReactElement;
|
||||
Component: PreferencePageComponent;
|
||||
}
|
||||
@ -47,7 +48,7 @@ export interface PreferenceGroup {
|
||||
kind: "group";
|
||||
id: string;
|
||||
parentId: string;
|
||||
isShown?: boolean;
|
||||
isShown?: IComputedValue<boolean> | boolean;
|
||||
childrenSeparator?: () => React.ReactElement;
|
||||
}
|
||||
|
||||
@ -57,7 +58,7 @@ export interface PreferenceItem {
|
||||
id: string;
|
||||
parentId: string;
|
||||
orderNumber: number;
|
||||
isShown?: boolean;
|
||||
isShown?: IComputedValue<boolean> | boolean;
|
||||
childrenSeparator?: () => React.ReactElement;
|
||||
}
|
||||
|
||||
|
||||
@ -2,13 +2,15 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { computed } from "mobx";
|
||||
import React from "react";
|
||||
|
||||
export interface PreferenceTabsRoot {
|
||||
kind: "preference-tabs-root";
|
||||
id: string;
|
||||
parentId: undefined;
|
||||
isShown: true;
|
||||
isShown: IComputedValue<true>;
|
||||
childrenSeparator: () => React.ReactElement;
|
||||
}
|
||||
|
||||
@ -16,6 +18,6 @@ export const preferenceTabsRoot: PreferenceTabsRoot = {
|
||||
kind: "preference-tabs-root" as const,
|
||||
id: "preference-tabs",
|
||||
parentId: undefined,
|
||||
isShown: true,
|
||||
isShown: computed(() => true as const),
|
||||
childrenSeparator: () => <hr />,
|
||||
};
|
||||
|
||||
@ -11,6 +11,8 @@ import getComposite from "../../../application-menu/main/menu-items/get-composit
|
||||
import { filter } from "lodash/fp";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { preferenceTabsRoot } from "./preference-tab-root";
|
||||
import logErrorInjectable from "../../../../common/log-error.injectable";
|
||||
import { isBoolean } from "../../../../common/utils";
|
||||
|
||||
const preferencesCompositeInjectable = getInjectable({
|
||||
id: "preferences-composite",
|
||||
@ -18,17 +20,41 @@ const preferencesCompositeInjectable = getInjectable({
|
||||
instantiate: (di) => {
|
||||
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
||||
const preferenceItems = computedInjectMany(preferenceItemInjectionToken);
|
||||
const logError = di.inject(logErrorInjectable);
|
||||
|
||||
return computed(() =>
|
||||
pipeline(
|
||||
[preferenceTabsRoot, ...preferenceItems.get()],
|
||||
filter(isShown),
|
||||
(items) => getComposite({ source: items }),
|
||||
|
||||
filter((item: PreferenceTypes) => {
|
||||
if (item.isShown === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isBoolean(item.isShown)) {
|
||||
return item.isShown;
|
||||
}
|
||||
|
||||
return item.isShown.get();
|
||||
}),
|
||||
|
||||
(items) =>
|
||||
getComposite({
|
||||
source: items,
|
||||
|
||||
handleMissingParentIds: (ids) => {
|
||||
logError(
|
||||
`Tried to create preferences, but encountered references to unknown ids: "${ids.missingParentIds.join(
|
||||
'", "',
|
||||
)}". Available ids are: "${ids.availableParentIds.join(
|
||||
'", "',
|
||||
)}"`,
|
||||
);
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const isShown = (item: PreferenceTypes) => item.isShown ?? true;
|
||||
|
||||
export default preferencesCompositeInjectable;
|
||||
|
||||
@ -63,8 +63,8 @@ const toNavigationHierarchy = (composite: Composite<PreferenceTypes | Preference
|
||||
|
||||
case "tab-group": {
|
||||
return (
|
||||
<>
|
||||
<div className="header flex items-center" data-preference-tab-group-test={value.id}>
|
||||
<div data-preference-tab-group-test={value.id}>
|
||||
<div className="header flex items-center">
|
||||
{value.iconName && (
|
||||
<Icon
|
||||
material={value.iconName}
|
||||
@ -76,7 +76,7 @@ const toNavigationHierarchy = (composite: Composite<PreferenceTypes | Preference
|
||||
</div>
|
||||
|
||||
<Map items={composite.children}>{toNavigationHierarchy}</Map>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ import { PreferencesNavigation } from "./preference-navigation/preferences-navig
|
||||
|
||||
interface Dependencies {
|
||||
closePreferences: () => void;
|
||||
pageComposite: IComputedValue<Composite<PreferenceTab>>;
|
||||
pageComposite: IComputedValue<Composite<PreferenceTab> | undefined>;
|
||||
}
|
||||
|
||||
const NonInjectedPreferences = observer(({
|
||||
@ -35,7 +35,16 @@ const NonInjectedPreferences = observer(({
|
||||
closeButtonProps={{ "data-testid": "close-preferences" }}
|
||||
back={closePreferences}
|
||||
>
|
||||
{toPreferenceItemHierarchy(composite)}
|
||||
{composite ? (
|
||||
toPreferenceItemHierarchy(composite)
|
||||
) : (
|
||||
<div
|
||||
className="flex items-center"
|
||||
data-preference-page-does-not-exist-test={true}
|
||||
>
|
||||
No preferences found
|
||||
</div>
|
||||
)}
|
||||
</SettingLayout>
|
||||
);
|
||||
});
|
||||
@ -58,13 +67,14 @@ const toPreferenceItemHierarchy = (composite: Composite<PreferenceTypes>) => {
|
||||
const Component = value.Component;
|
||||
|
||||
return (
|
||||
<Component>
|
||||
<Map items={composite.children} getSeparator={value.childrenSeparator}>
|
||||
{toPreferenceItemHierarchy}
|
||||
</Map>
|
||||
</Component>
|
||||
<div data-preference-item-test={composite.id}>
|
||||
<Component>
|
||||
<Map items={composite.children} getSeparator={value.childrenSeparator}>
|
||||
{toPreferenceItemHierarchy}
|
||||
</Map>
|
||||
</Component>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration";
|
||||
import type { DOMAttributes } from "react";
|
||||
import React from "react";
|
||||
import { cssNames } from "../../../renderer/utils";
|
||||
|
||||
export interface ExtensionSettingsProps extends DOMAttributes<HTMLDivElement> {
|
||||
setting: AppPreferenceRegistration;
|
||||
size: "small" | "normal";
|
||||
}
|
||||
|
||||
export function ExtensionSettings({ setting, size, ...props }: ExtensionSettingsProps) {
|
||||
const {
|
||||
title,
|
||||
id,
|
||||
components: { Hint, Input },
|
||||
} = setting;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<section
|
||||
id={id}
|
||||
className={cssNames(size)}
|
||||
{...props}
|
||||
>
|
||||
<SubTitle title={title} />
|
||||
<Input />
|
||||
<div className="hint">
|
||||
<Hint />
|
||||
</div>
|
||||
</section>
|
||||
<hr className={cssNames(size)} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@ -9,7 +9,7 @@ import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
import extensionPreferencesModelInjectable from "./extension-preference-model.injectable";
|
||||
import { ExtensionSettings } from "./extension-settings";
|
||||
import { ExtensionPreferenceItem } from "../../../features/preferences/renderer/compliance-for-legacy-extension-api/extension-preference-item";
|
||||
|
||||
interface Dependencies {
|
||||
model: IComputedValue<{
|
||||
@ -36,10 +36,9 @@ const NonInjectedExtensions = ({ model }: Dependencies) => {
|
||||
</div>
|
||||
)}
|
||||
{preferenceItems.map((preferenceItem, index) => (
|
||||
<ExtensionSettings
|
||||
<ExtensionPreferenceItem
|
||||
key={`${preferenceItem.id}-${index}`}
|
||||
setting={preferenceItem}
|
||||
size="small"
|
||||
registration={preferenceItem}
|
||||
data-testid={`extension-preference-item-for-${preferenceItem.id}`}
|
||||
/>
|
||||
))}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user