Skip to content

Commit f80ae0f

Browse files
committed
feat: add 'Start my first pod' button for empty pods list page
Signed-off-by: Denis Golovin <[email protected]>
1 parent 6edb05e commit f80ae0f

File tree

2 files changed

+181
-3
lines changed

2 files changed

+181
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**********************************************************************
2+
* Copyright (C) 2024 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
***********************************************************************/
18+
19+
import '@testing-library/jest-dom/vitest';
20+
21+
import { fireEvent, render, screen } from '@testing-library/svelte';
22+
import { afterEach, beforeAll, expect, test, vi } from 'vitest';
23+
24+
import { providerInfos } from '/@/stores/providers';
25+
import type { ImageInfo } from '/@api/image-info';
26+
import type { ProviderContainerConnectionInfo, ProviderInfo } from '/@api/provider-info';
27+
28+
import PodEmptyScreen from './PodEmptyScreen.svelte';
29+
30+
beforeAll(() => {
31+
(window as any).createPod = vi.fn();
32+
(window as any).showMessageBox = vi.fn();
33+
(window as any).clipboardWriteText = vi.fn();
34+
(window as any).pullImage = vi.fn();
35+
(window as any).listImages = vi.fn();
36+
(window as any).createAndStartContainer = vi.fn();
37+
providerInfos.set([
38+
{
39+
containerConnections: [
40+
{
41+
status: 'started',
42+
} as unknown as ProviderContainerConnectionInfo,
43+
],
44+
} as unknown as ProviderInfo,
45+
]);
46+
});
47+
48+
afterEach(() => {
49+
vi.resetAllMocks();
50+
});
51+
52+
const helloImage = 'quay.io/podman/hello:latest';
53+
const error = new Error('Error message');
54+
const errorMessage = {
55+
title: 'Error when running a pod',
56+
message: String(error),
57+
};
58+
const imageErrorMessage = {
59+
title: `Error when running a pod`,
60+
message: `Could not find '${helloImage}' in images`,
61+
};
62+
const providerErrorMessage = {
63+
title: `Error when running a pod`,
64+
message: `No provider connections found`,
65+
};
66+
const buttonName = 'Start your first pod';
67+
const getButton = screen.getByRole.bind(screen, 'button', { name: buttonName });
68+
const copyToClipboard = 'Copy To Clipboard';
69+
const imageInfo = {
70+
engineId: 'engineId',
71+
RepoTags: [helloImage],
72+
} as ImageInfo;
73+
const podInfo = { engineId: imageInfo.engineId, Id: 'Id' };
74+
const podCreateCommand = `podman run -dt --pod new:myFirstPod ${helloImage}`;
75+
76+
function testComponent(name: string, fn: () => Promise<unknown>) {
77+
test(name, () => {
78+
vi.mocked(window.listImages).mockResolvedValue([imageInfo]);
79+
render(PodEmptyScreen);
80+
return fn();
81+
});
82+
}
83+
84+
testComponent('renders button to run first pod', async () => {
85+
expect(getButton()).toBeVisible();
86+
});
87+
88+
testComponent('button click creates and starts a pod', async () => {
89+
vi.spyOn(window, 'createPod').mockResolvedValue(podInfo);
90+
await fireEvent.click(getButton());
91+
expect(window.createPod).toBeCalledWith({ name: 'myFirstPod' });
92+
expect(window.createAndStartContainer).toBeCalledWith(podInfo.engineId, {
93+
Image: helloImage,
94+
pod: 'myFirstPod',
95+
});
96+
});
97+
98+
testComponent('button click shows error message if creating pod fails', async () => {
99+
vi.spyOn(window, 'createPod').mockRejectedValue(error);
100+
await fireEvent.click(getButton());
101+
expect(window.showMessageBox).toBeCalledWith(errorMessage);
102+
});
103+
104+
testComponent('button click shows error message if starting pod fails', async () => {
105+
vi.spyOn(window, 'createPod').mockResolvedValue(podInfo);
106+
vi.spyOn(window, 'createAndStartContainer').mockRejectedValue(error);
107+
await fireEvent.click(getButton());
108+
vi.waitFor(() => expect(window.showMessageBox).toBeCalledWith(errorMessage));
109+
});
110+
111+
test('button click shows error if image could not be pulled', async () => {
112+
vi.mocked(window.listImages).mockResolvedValue([]);
113+
render(PodEmptyScreen);
114+
await fireEvent.click(getButton());
115+
vi.waitFor(() => expect(window.showMessageBox).toBeCalledWith(imageErrorMessage));
116+
});
117+
118+
testComponent('button click shows error message if there is no active provider connection', async () => {
119+
providerInfos.set([]);
120+
await fireEvent.click(getButton());
121+
vi.waitFor(() => expect(window.showMessageBox).toBeCalledWith(providerErrorMessage));
122+
});
123+
124+
testComponent(`${copyToClipboard} button click puts starting pod command to clipboard`, async () => {
125+
await fireEvent.click(screen.getByTitle(copyToClipboard));
126+
expect(window.clipboardWriteText).toBeCalledWith(podCreateCommand);
127+
});
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,65 @@
11
<script lang="ts">
2-
import { EmptyScreen } from '@podman-desktop/ui-svelte';
2+
import { Button, EmptyScreen } from '@podman-desktop/ui-svelte';
3+
4+
import { providerInfos } from '/@/stores/providers';
35
46
import PodIcon from '../images/PodIcon.svelte';
57
6-
const commandLine = 'podman pod create --label myFirstPod';
8+
const myFirstPod = 'myFirstPod';
9+
const helloImage = 'quay.io/podman/hello:latest';
10+
const commandLine = `podman run -dt --pod new:${myFirstPod} ${helloImage}`;
11+
12+
let inProgress = false;
13+
14+
$: selectedProviderConnection = $providerInfos
15+
.map(provider => provider.containerConnections)
16+
.flat()
17+
.find(providerContainerConnection => providerContainerConnection.status === 'started');
18+
19+
async function startPod(): Promise<void> {
20+
inProgress = true;
21+
if (selectedProviderConnection) {
22+
try {
23+
await window.pullImage(selectedProviderConnection, helloImage, () => {});
24+
const listImages = await window.listImages();
25+
const image = listImages.find(item => item.RepoTags?.includes(helloImage));
26+
await window.createPod({ name: myFirstPod });
27+
if (image) {
28+
await window.createAndStartContainer(image.engineId, { Image: helloImage, pod: myFirstPod });
29+
} else {
30+
await window.showMessageBox({
31+
title: `Error when running a pod`,
32+
message: `Could not find '${helloImage}'' in images`,
33+
});
34+
}
35+
} catch (error) {
36+
await window.showMessageBox({
37+
title: `Error when running a pod`,
38+
message: String(error),
39+
});
40+
} finally {
41+
inProgress = false;
42+
}
43+
} else {
44+
await window.showMessageBox({
45+
title: `Error when running a pod`,
46+
message: `No provider connections found`,
47+
});
48+
}
49+
}
750
</script>
851

952
<EmptyScreen
1053
icon={PodIcon}
1154
title="No pods"
1255
message="Run a first pod using the following command line:"
1356
commandline={commandLine}
14-
on:click={() => window.clipboardWriteText(commandLine)} />
57+
on:click={() => window.clipboardWriteText(commandLine)}>
58+
<div slot="upperContent">
59+
<div class="flex gap-2 justify-center p-3">
60+
<Button title="Start your first pod" type="primary" inProgress={inProgress} on:click={() => startPod()}
61+
>Start your first pod</Button>
62+
</div>
63+
<h1 class="text-xl text-[var(--pd-details-empty-header)]">OR</h1>
64+
</div>
65+
</EmptyScreen>

0 commit comments

Comments
 (0)