mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add implementation for asking boolean over processes
Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
1b46ccd18b
commit
e2de1449d5
@ -300,7 +300,12 @@ describe("installing update using tray", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("asks user to install update immediately", () => {
|
it("asks user to install update immediately", () => {
|
||||||
expect(askBooleanMock).toHaveBeenCalledWith("Do you want to install update some-version?");
|
expect(askBooleanMock).toHaveBeenCalledWith({
|
||||||
|
id: "install-update",
|
||||||
|
title: "Update Available",
|
||||||
|
question:
|
||||||
|
"Version some-version of Lens IDE is available and ready to be installed. Would you like to update now?",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when user answers to install the update", () => {
|
describe("when user answers to install the update", () => {
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { Channel } from "../channel/channel-injection-token";
|
||||||
|
import { channelInjectionToken } from "../channel/channel-injection-token";
|
||||||
|
|
||||||
|
type AskBooleanAnswerChannel = Channel<{ id: string; value: boolean }>;
|
||||||
|
|
||||||
|
const askBooleanAnswerChannelInjectable = getInjectable({
|
||||||
|
id: "ask-boolean-answer-channel",
|
||||||
|
|
||||||
|
instantiate: (): AskBooleanAnswerChannel => ({
|
||||||
|
id: "ask-boolean-answer",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: channelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default askBooleanAnswerChannelInjectable;
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { Channel } from "../channel/channel-injection-token";
|
||||||
|
import { channelInjectionToken } from "../channel/channel-injection-token";
|
||||||
|
|
||||||
|
export interface AskBooleanQuestionParameters { id: string; title: string; question: string }
|
||||||
|
export type AskBooleanQuestionChannel = Channel<AskBooleanQuestionParameters>;
|
||||||
|
|
||||||
|
const askBooleanQuestionChannelInjectable = getInjectable({
|
||||||
|
id: "ask-boolean-question-channel",
|
||||||
|
|
||||||
|
instantiate: (): AskBooleanQuestionChannel => ({
|
||||||
|
id: "ask-boolean-question",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: channelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default askBooleanQuestionChannelInjectable;
|
||||||
336
src/main/ask-boolean/__snapshots__/ask-boolean.test.ts.snap
Normal file
336
src/main/ask-boolean/__snapshots__/ask-boolean.test.ts.snap
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`ask-boolean given started when asking multiple questions renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex column gaps"
|
||||||
|
data-testid="ask-boolean-some-question-id"
|
||||||
|
>
|
||||||
|
<b>
|
||||||
|
some-title
|
||||||
|
</b>
|
||||||
|
<p>
|
||||||
|
Some question
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="flex gaps row align-left box grow"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="Button light"
|
||||||
|
data-testid="ask-boolean-some-question-id-button-yes"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="Button active outlined"
|
||||||
|
data-testid="ask-boolean-some-question-id-button-no"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-ask-boolean-for-some-question-id"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex column gaps"
|
||||||
|
data-testid="ask-boolean-some-other-question-id"
|
||||||
|
>
|
||||||
|
<b>
|
||||||
|
some-other-title
|
||||||
|
</b>
|
||||||
|
<p>
|
||||||
|
Some other question
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="flex gaps row align-left box grow"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="Button light"
|
||||||
|
data-testid="ask-boolean-some-other-question-id-button-yes"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="Button active outlined"
|
||||||
|
data-testid="ask-boolean-some-other-question-id-button-no"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-ask-boolean-for-some-other-question-id"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`ask-boolean given started when asking multiple questions when answering to first question renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex column gaps"
|
||||||
|
data-testid="ask-boolean-some-other-question-id"
|
||||||
|
>
|
||||||
|
<b>
|
||||||
|
some-other-title
|
||||||
|
</b>
|
||||||
|
<p>
|
||||||
|
Some other question
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="flex gaps row align-left box grow"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="Button light"
|
||||||
|
data-testid="ask-boolean-some-other-question-id-button-yes"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="Button active outlined"
|
||||||
|
data-testid="ask-boolean-some-other-question-id-button-no"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-ask-boolean-for-some-other-question-id"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`ask-boolean given started when asking question renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex column gaps"
|
||||||
|
data-testid="ask-boolean-some-question-id"
|
||||||
|
>
|
||||||
|
<b>
|
||||||
|
some-title
|
||||||
|
</b>
|
||||||
|
<p>
|
||||||
|
Some question
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="flex gaps row align-left box grow"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="Button light"
|
||||||
|
data-testid="ask-boolean-some-question-id-button-yes"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="Button active outlined"
|
||||||
|
data-testid="ask-boolean-some-question-id-button-no"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-ask-boolean-for-some-question-id"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`ask-boolean given started when asking question when user answers "no" renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`ask-boolean given started when asking question when user answers "yes" renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`ask-boolean given started when asking question when user closes notification without answering the question renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
37
src/main/ask-boolean/ask-boolean-promise.injectable.ts
Normal file
37
src/main/ask-boolean/ask-boolean-promise.injectable.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
|
const askBooleanPromiseInjectable = getInjectable({
|
||||||
|
id: "ask-boolean-promise",
|
||||||
|
|
||||||
|
instantiate: (di, questionId: string) => {
|
||||||
|
void questionId;
|
||||||
|
|
||||||
|
let resolve: (value: boolean) => void;
|
||||||
|
let _promise: Promise<boolean>;
|
||||||
|
|
||||||
|
return ({
|
||||||
|
get promise() {
|
||||||
|
return _promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: () => {
|
||||||
|
_promise = new Promise(_resolve => {
|
||||||
|
resolve = _resolve;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
resolve: (value: boolean) => {
|
||||||
|
resolve(value); },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
lifecycle: lifecycleEnum.keyedSingleton({
|
||||||
|
getInstanceKey: (di, questionId: string) => questionId,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default askBooleanPromiseInjectable;
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { channelListenerInjectionToken } from "../../common/channel/channel-listener-injection-token";
|
||||||
|
import askBooleanAnswerChannelInjectable from "../../common/ask-boolean/ask-boolean-answer-channel.injectable";
|
||||||
|
import askBooleanPromiseInjectable from "./ask-boolean-promise.injectable";
|
||||||
|
|
||||||
|
const askBooleanReturnValueListenerInjectable = getInjectable({
|
||||||
|
id: "ask-boolean-return-value-listener",
|
||||||
|
|
||||||
|
instantiate: (di) => ({
|
||||||
|
channel: di.inject(askBooleanAnswerChannelInjectable),
|
||||||
|
|
||||||
|
handler: ({ id, value }) => {
|
||||||
|
const returnValuePromise = di.inject(askBooleanPromiseInjectable, id);
|
||||||
|
|
||||||
|
returnValuePromise.resolve(value);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: channelListenerInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default askBooleanReturnValueListenerInjectable;
|
||||||
@ -3,12 +3,37 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { sendToAgnosticChannelInjectionToken } from "../../common/channel/send-to-agnostic-channel-injection-token";
|
||||||
|
import askBooleanQuestionChannelInjectable from "../../common/ask-boolean/ask-boolean-question-channel.injectable";
|
||||||
|
import askBooleanPromiseInjectable from "./ask-boolean-promise.injectable";
|
||||||
|
|
||||||
export type AskBoolean = (question: string) => Promise<boolean>;
|
export type AskBoolean = ({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
question,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
question: string;
|
||||||
|
}) => Promise<boolean>;
|
||||||
|
|
||||||
const askBooleanInjectable = getInjectable({
|
const askBooleanInjectable = getInjectable({
|
||||||
id: "ask-boolean",
|
id: "ask-boolean",
|
||||||
instantiate: (di): AskBoolean => async (question: string) => false,
|
|
||||||
|
instantiate: (di): AskBoolean => {
|
||||||
|
const sendToAgnosticChannel = di.inject(sendToAgnosticChannelInjectionToken);
|
||||||
|
const askBooleanChannel = di.inject(askBooleanQuestionChannelInjectable);
|
||||||
|
|
||||||
|
return async ({ id, title, question }) => {
|
||||||
|
const returnValuePromise = di.inject(askBooleanPromiseInjectable, id);
|
||||||
|
|
||||||
|
returnValuePromise.clear();
|
||||||
|
|
||||||
|
await sendToAgnosticChannel(askBooleanChannel, { id, title, question });
|
||||||
|
|
||||||
|
return await returnValuePromise.promise;
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default askBooleanInjectable;
|
export default askBooleanInjectable;
|
||||||
|
|||||||
201
src/main/ask-boolean/ask-boolean.test.ts
Normal file
201
src/main/ask-boolean/ask-boolean.test.ts
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import type { AskBoolean } from "./ask-boolean.injectable";
|
||||||
|
import askBooleanInjectable from "./ask-boolean.injectable";
|
||||||
|
import { getPromiseStatus } from "../../common/test-utils/get-promise-status";
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import { fireEvent } from "@testing-library/react";
|
||||||
|
|
||||||
|
describe("ask-boolean", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let askBoolean: AskBoolean;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
askBoolean = applicationBuilder.dis.mainDi.inject(askBooleanInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given started", () => {
|
||||||
|
let rendered: RenderResult;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when asking question", () => {
|
||||||
|
let actualPromise: Promise<boolean>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
actualPromise = askBoolean({
|
||||||
|
id: "some-question-id",
|
||||||
|
title: "some-title",
|
||||||
|
question: "Some question",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not resolve yet", async () => {
|
||||||
|
const promiseStatus = await getPromiseStatus(actualPromise);
|
||||||
|
|
||||||
|
expect(promiseStatus.fulfilled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows notification", () => {
|
||||||
|
const notification = rendered.getByTestId("ask-boolean-some-question-id");
|
||||||
|
|
||||||
|
expect(notification).not.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when user answers "yes"', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("ask-boolean-some-question-id-button-yes");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show notification anymore", () => {
|
||||||
|
const notification = rendered.queryByTestId("ask-boolean-some-question-id");
|
||||||
|
|
||||||
|
expect(notification).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves", async () => {
|
||||||
|
const actual = await actualPromise;
|
||||||
|
|
||||||
|
expect(actual).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when user answers "no"', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("ask-boolean-some-question-id-button-no");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show notification anymore", () => {
|
||||||
|
const notification = rendered.queryByTestId("ask-boolean-some-question-id");
|
||||||
|
|
||||||
|
expect(notification).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves", async () => {
|
||||||
|
const actual = await actualPromise;
|
||||||
|
|
||||||
|
expect(actual).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when user closes notification without answering the question", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("close-notification-for-ask-boolean-for-some-question-id");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show notification anymore", () => {
|
||||||
|
const notification = rendered.queryByTestId("ask-boolean-some-question-id");
|
||||||
|
|
||||||
|
expect(notification).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves", async () => {
|
||||||
|
const actual = await actualPromise;
|
||||||
|
|
||||||
|
expect(actual).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when asking multiple questions", () => {
|
||||||
|
let firstQuestionPromise: Promise<boolean>;
|
||||||
|
let secondQuestionPromise: Promise<boolean>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
firstQuestionPromise = askBoolean({
|
||||||
|
id: "some-question-id",
|
||||||
|
title: "some-title",
|
||||||
|
question: "Some question",
|
||||||
|
});
|
||||||
|
|
||||||
|
secondQuestionPromise = askBoolean({
|
||||||
|
id: "some-other-question-id",
|
||||||
|
title: "some-other-title",
|
||||||
|
question: "Some other question",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows notification for first question", () => {
|
||||||
|
const notification = rendered.getByTestId("ask-boolean-some-question-id");
|
||||||
|
|
||||||
|
expect(notification).not.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows notification for second question", () => {
|
||||||
|
const notification = rendered.getByTestId("ask-boolean-some-other-question-id");
|
||||||
|
|
||||||
|
expect(notification).not.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when answering to first question", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const button = rendered.getByTestId("ask-boolean-some-question-id-button-no");
|
||||||
|
|
||||||
|
fireEvent.click(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show notification for first question anymore", () => {
|
||||||
|
const notification = rendered.queryByTestId("ask-boolean-some-question-id");
|
||||||
|
|
||||||
|
expect(notification).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("still shows notification for second question", () => {
|
||||||
|
const notification = rendered.getByTestId("ask-boolean-some-other-question-id");
|
||||||
|
|
||||||
|
expect(notification).not.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves first question", async () => {
|
||||||
|
const actual = await firstQuestionPromise;
|
||||||
|
|
||||||
|
expect(actual).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not resolve second question yet", async () => {
|
||||||
|
const promiseStatus = await getPromiseStatus(secondQuestionPromise);
|
||||||
|
|
||||||
|
expect(promiseStatus.fulfilled).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -76,7 +76,11 @@ const checkForUpdatesTrayItemInjectable = getInjectable({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userWantsToInstallUpdate = await askBoolean(`Do you want to install update ${version}?`);
|
const userWantsToInstallUpdate = await askBoolean({
|
||||||
|
id: "install-update",
|
||||||
|
title: "Update available",
|
||||||
|
question: `Version ${version} of Lens IDE is available and ready to be installed. Would you like to update now?`,
|
||||||
|
});
|
||||||
|
|
||||||
if (userWantsToInstallUpdate) {
|
if (userWantsToInstallUpdate) {
|
||||||
quitAndInstallUpdate();
|
quitAndInstallUpdate();
|
||||||
|
|||||||
@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { channelListenerInjectionToken } from "../../common/channel/channel-listener-injection-token";
|
||||||
|
import type { AskBooleanQuestionParameters } from "../../common/ask-boolean/ask-boolean-question-channel.injectable";
|
||||||
|
import askBooleanQuestionChannelInjectable from "../../common/ask-boolean/ask-boolean-question-channel.injectable";
|
||||||
|
import showInfoNotificationInjectable from "../components/notifications/show-info-notification.injectable";
|
||||||
|
import { Button } from "../components/button";
|
||||||
|
import React from "react";
|
||||||
|
import { sendToAgnosticChannelInjectionToken } from "../../common/channel/send-to-agnostic-channel-injection-token";
|
||||||
|
import askBooleanAnswerChannelInjectable from "../../common/ask-boolean/ask-boolean-answer-channel.injectable";
|
||||||
|
import notificationsStoreInjectable from "../components/notifications/notifications-store.injectable";
|
||||||
|
|
||||||
|
const askBooleanQuestionChannelListenerInjectable = getInjectable({
|
||||||
|
id: "ask-boolean-question-channel-listener",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const questionChannel = di.inject(askBooleanQuestionChannelInjectable);
|
||||||
|
const showInfoNotification = di.inject(showInfoNotificationInjectable);
|
||||||
|
const sendToAgnosticChannel = di.inject(sendToAgnosticChannelInjectionToken);
|
||||||
|
const answerChannel = di.inject(askBooleanAnswerChannelInjectable);
|
||||||
|
const notificationsStore = di.inject(notificationsStoreInjectable);
|
||||||
|
|
||||||
|
const sendAnswerFor = (id: string) => (value: boolean) => {
|
||||||
|
sendToAgnosticChannel(answerChannel, { id, value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeNotification = (notificationId: string) => {
|
||||||
|
notificationsStore.remove(notificationId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendAnswerAndCloseNotificationFor = (sendAnswer: (value: boolean) => void, notificationId: string) => (value: boolean) => () => {
|
||||||
|
sendAnswer(value);
|
||||||
|
closeNotification(notificationId);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
channel: questionChannel,
|
||||||
|
|
||||||
|
handler: ({ id: questionId, title, question }: AskBooleanQuestionParameters) => {
|
||||||
|
const notificationId = `ask-boolean-for-${questionId}`;
|
||||||
|
|
||||||
|
const sendAnswer = sendAnswerFor(questionId);
|
||||||
|
const sendAnswerAndCloseNotification = sendAnswerAndCloseNotificationFor(sendAnswer, notificationId);
|
||||||
|
|
||||||
|
showInfoNotification(
|
||||||
|
<AskBoolean
|
||||||
|
id={questionId}
|
||||||
|
title={title}
|
||||||
|
message={question}
|
||||||
|
onNo={sendAnswerAndCloseNotification(false)}
|
||||||
|
onYes={sendAnswerAndCloseNotification(true)}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
{
|
||||||
|
id: notificationId,
|
||||||
|
timeout: 0,
|
||||||
|
onClose: () => sendAnswer(false),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: channelListenerInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default askBooleanQuestionChannelListenerInjectable;
|
||||||
|
|
||||||
|
const AskBoolean = ({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
onNo,
|
||||||
|
onYes,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
onNo: () => void;
|
||||||
|
onYes: () => void;
|
||||||
|
}) => (
|
||||||
|
<div className="flex column gaps" data-testid={`ask-boolean-${id}`}>
|
||||||
|
<b>{title}</b>
|
||||||
|
<p>{message}</p>
|
||||||
|
|
||||||
|
<div className="flex gaps row align-left box grow">
|
||||||
|
<Button
|
||||||
|
light
|
||||||
|
label="Yes"
|
||||||
|
onClick={onYes}
|
||||||
|
data-testid={`ask-boolean-${id}-button-yes`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
active
|
||||||
|
outlined
|
||||||
|
label="No"
|
||||||
|
data-testid={`ask-boolean-${id}-button-no`}
|
||||||
|
onClick={onNo}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
@ -3,19 +3,24 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { notificationsStore, NotificationStatus } from "./notifications.store";
|
import type { NotificationMessage, Notification } from "./notifications.store";
|
||||||
|
import { NotificationStatus } from "./notifications.store";
|
||||||
|
import notificationsStoreInjectable from "./notifications-store.injectable";
|
||||||
|
|
||||||
const showInfoNotificationInjectable = getInjectable({
|
const showInfoNotificationInjectable = getInjectable({
|
||||||
id: "show-info-notification",
|
id: "show-info-notification",
|
||||||
|
|
||||||
instantiate: () => (message: string) =>
|
instantiate: (di) => {
|
||||||
|
const notificationsStore = di.inject(notificationsStoreInjectable);
|
||||||
|
|
||||||
|
return (message: NotificationMessage, customOpts: Partial<Omit<Notification, "message">> = {}) =>
|
||||||
notificationsStore.add({
|
notificationsStore.add({
|
||||||
status: NotificationStatus.INFO,
|
status: NotificationStatus.INFO,
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
message,
|
message,
|
||||||
}),
|
...customOpts,
|
||||||
|
});
|
||||||
causesSideEffects: true,
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default showInfoNotificationInjectable;
|
export default showInfoNotificationInjectable;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user