Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixes #925 Signed-off-by: Florent Benoit <fbenoit@redhat.com>
- Loading branch information
Showing
5 changed files
with
361 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
/********************************************************************** | ||
* Copyright (C) 2023 Red Hat, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
***********************************************************************/ | ||
|
||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
|
||
import '@testing-library/jest-dom/vitest'; | ||
import { test, expect, vi, beforeAll } from 'vitest'; | ||
import { render, screen } from '@testing-library/svelte'; | ||
import { providerInfos } from '../../stores/providers'; | ||
import type { ProviderInfo } from '../../../../main/src/plugin/api/provider-info'; | ||
import userEvent from '@testing-library/user-event'; | ||
import CreateVolume from './CreateVolume.svelte'; | ||
|
||
const createVolumeMock = vi.fn(); | ||
|
||
// fake the window.events object | ||
beforeAll(() => { | ||
(window as any).createVolume = createVolumeMock; | ||
}); | ||
|
||
const createButtonTitle = 'Create'; | ||
|
||
test('Expect no create button with no providers', async () => { | ||
providerInfos.set([]); | ||
|
||
render(CreateVolume, {}); | ||
|
||
// no button | ||
const createButton = screen.queryByRole('button', { name: createButtonTitle }); | ||
expect(createButton).not.toBeInTheDocument(); | ||
|
||
// expect empty screen | ||
const emptyScreen = screen.getByText('No Container Engine'); | ||
expect(emptyScreen).toBeInTheDocument(); | ||
|
||
// expect that we never call | ||
expect(createVolumeMock).not.toBeCalled(); | ||
}); | ||
|
||
test('Expect Create button is working', async () => { | ||
providerInfos.set([ | ||
{ | ||
name: 'podman', | ||
status: 'started', | ||
internalId: 'podman-internal-id', | ||
containerConnections: [ | ||
{ | ||
name: 'podman-machine-default', | ||
status: 'started', | ||
}, | ||
], | ||
} as unknown as ProviderInfo, | ||
]); | ||
|
||
render(CreateVolume, {}); | ||
|
||
const createButton = screen.getByRole('button', { name: createButtonTitle }); | ||
expect(createButton).toBeInTheDocument(); | ||
expect(createButton).toBeEnabled(); | ||
|
||
// click on it | ||
await userEvent.click(createButton); | ||
|
||
// expect that we called createVolume API | ||
expect(createVolumeMock).toHaveBeenCalledWith(expect.anything(), { Name: '' }); | ||
}); | ||
|
||
test('Expect Create with a custom name', async () => { | ||
providerInfos.set([ | ||
{ | ||
name: 'podman', | ||
status: 'started', | ||
internalId: 'podman-internal-id', | ||
containerConnections: [ | ||
{ | ||
name: 'podman-machine-default', | ||
status: 'started', | ||
}, | ||
], | ||
} as unknown as ProviderInfo, | ||
]); | ||
|
||
render(CreateVolume, {}); | ||
|
||
const createButton = screen.getByRole('button', { name: createButtonTitle }); | ||
expect(createButton).toBeInTheDocument(); | ||
expect(createButton).toBeEnabled(); | ||
|
||
// expect no combo box as only one provider | ||
const providerSelect = screen.queryByRole('combobox', { name: 'Provider Choice' }); | ||
expect(providerSelect).not.toBeInTheDocument(); | ||
|
||
// grab input field | ||
const nameInput = screen.getByRole('textbox', { name: 'Volume Name' }); | ||
expect(nameInput).toBeInTheDocument(); | ||
expect(nameInput).toBeEnabled(); | ||
|
||
const customVolumeName = 'my-custom-volume'; | ||
|
||
// enter the text 'my-custom-volume | ||
await userEvent.type(nameInput, customVolumeName); | ||
|
||
// click on it | ||
await userEvent.click(createButton); | ||
|
||
// expect that we called createVolume API | ||
expect(createVolumeMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'podman-machine-default' }), { | ||
Name: customVolumeName, | ||
}); | ||
}); | ||
|
||
test('Expect Create with a custom name and multiple providers', async () => { | ||
providerInfos.set([ | ||
{ | ||
name: 'podman', | ||
status: 'started', | ||
internalId: 'podman-internal-id', | ||
containerConnections: [ | ||
{ | ||
name: 'podman-machine-default', | ||
status: 'started', | ||
}, | ||
], | ||
} as unknown as ProviderInfo, | ||
{ | ||
name: 'docker', | ||
status: 'started', | ||
internalId: 'docker-internal-id', | ||
containerConnections: [ | ||
{ | ||
name: 'docker', | ||
status: 'started', | ||
}, | ||
], | ||
} as unknown as ProviderInfo, | ||
]); | ||
|
||
render(CreateVolume, {}); | ||
|
||
const createButton = screen.getByRole('button', { name: createButtonTitle }); | ||
expect(createButton).toBeInTheDocument(); | ||
expect(createButton).toBeEnabled(); | ||
|
||
// change the provider's choice | ||
const providerSelect = screen.getByRole('combobox', { name: 'Provider Choice' }); | ||
expect(providerSelect).toBeInTheDocument(); | ||
expect(providerSelect).toBeEnabled(); | ||
|
||
// change to the second provider | ||
await userEvent.selectOptions(providerSelect, 'docker'); | ||
|
||
// grab input field | ||
const nameInput = screen.getByRole('textbox', { name: 'Volume Name' }); | ||
expect(nameInput).toBeInTheDocument(); | ||
expect(nameInput).toBeEnabled(); | ||
|
||
const customVolumeName = 'my-custom-volume'; | ||
|
||
// enter the text 'my-custom-volume | ||
await userEvent.type(nameInput, customVolumeName); | ||
|
||
// click on it | ||
await userEvent.click(createButton); | ||
|
||
// expect that we called createVolume API with the docker provider as we changed the toggle | ||
expect(createVolumeMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'docker' }), { | ||
Name: customVolumeName, | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<script lang="ts"> | ||
/* eslint-disable import/no-duplicates */ | ||
// https://github.com/import-js/eslint-plugin-import/issues/1479 | ||
import { get } from 'svelte/store'; | ||
import { onDestroy, onMount } from 'svelte'; | ||
/* eslint-enable import/no-duplicates */ | ||
import type { ProviderContainerConnectionInfo, ProviderInfo } from '../../../../main/src/plugin/api/provider-info'; | ||
import { providerInfos } from '/@/stores/providers'; | ||
import FormPage from '/@/lib/ui/FormPage.svelte'; | ||
import NoContainerEngineEmptyScreen from '/@/lib/image/NoContainerEngineEmptyScreen.svelte'; | ||
import Button from '/@/lib/ui/Button.svelte'; | ||
import VolumeIcon from '/@/lib/images/VolumeIcon.svelte'; | ||
import { router } from 'tinro'; | ||
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons'; | ||
let providers: ProviderInfo[] = []; | ||
let providerConnections: ProviderContainerConnectionInfo[] = []; | ||
let selectedProvider: ProviderContainerConnectionInfo | undefined = undefined; | ||
let selectedProviderConnection: ProviderContainerConnectionInfo | undefined = undefined; | ||
onMount(async () => { | ||
providers = get(providerInfos); | ||
providerConnections = providers | ||
.map(provider => provider.containerConnections) | ||
.flat() | ||
.filter(providerContainerConnection => providerContainerConnection.status === 'started'); | ||
const selectedProviderConnection = providerConnections.length > 0 ? providerConnections[0] : undefined; | ||
selectedProvider = !selectedProvider && selectedProviderConnection ? selectedProviderConnection : selectedProvider; | ||
}); | ||
let createVolumeInProgress = false; | ||
onDestroy(() => {}); | ||
async function createVolume(providerConnectionInfo: ProviderContainerConnectionInfo) { | ||
createVolumeInProgress = true; | ||
try { | ||
await window.createVolume(providerConnectionInfo, { Name: volumeName }); | ||
} finally { | ||
createVolumeInProgress = false; | ||
createVolumeFinished = true; | ||
} | ||
} | ||
function end() { | ||
// redirect to the volumes page | ||
router.goto('/volumes'); | ||
} | ||
let createVolumeFinished = false; | ||
export let volumeName = ''; | ||
</script> | ||
|
||
<FormPage title="Create a volume"> | ||
<svelte:fragment slot="icon"> | ||
<VolumeIcon /> | ||
</svelte:fragment> | ||
<div slot="content" class="p-5 min-w-full h-fit"> | ||
{#if providerConnections.length === 0} | ||
<NoContainerEngineEmptyScreen /> | ||
{:else} | ||
<div class="bg-charcoal-900 pt-5 space-y-6 px-8 sm:pb-6 xl:pb-8 rounded-lg"> | ||
<div> | ||
<label for="containerBuildContextDirectory" class="block mb-2 text-sm font-bold text-gray-400" | ||
>Volume name:</label> | ||
<input | ||
aria-label="Volume Name" | ||
disabled="{createVolumeFinished}" | ||
bind:value="{volumeName}" | ||
class="w-full p-2 outline-none text-sm bg-charcoal-600 rounded-sm text-gray-700 placeholder-gray-700" | ||
required /> | ||
</div> | ||
<div class:hidden="{providerConnections.length < 2}"> | ||
{#if providerConnections.length > 1} | ||
<label for="providerChoice" class="py-3 block mb-2 text-sm font-bold text-gray-400 dark:text-gray-400" | ||
>Container Engine | ||
<select | ||
class="w-full p-2 outline-none text-sm bg-charcoal-600 rounded-sm text-gray-700 placeholder-gray-700" | ||
aria-label="Provider Choice" | ||
disabled="{createVolumeFinished}" | ||
bind:value="{selectedProvider}"> | ||
{#each providerConnections as providerConnection} | ||
<option value="{providerConnection}">{providerConnection.name}</option> | ||
{/each} | ||
</select> | ||
</label> | ||
{/if} | ||
</div> | ||
{#if providerConnections.length === 1 && selectedProviderConnection} | ||
<input type="hidden" aria-label="Provider Choice" readonly bind:value="{selectedProvider}" /> | ||
{/if} | ||
|
||
<div class="w-full flex flex-row space-x-4"> | ||
{#if !createVolumeFinished && selectedProvider} | ||
{@const connection = selectedProvider} | ||
<Button | ||
on:click="{() => createVolume(connection)}" | ||
disabled="{createVolumeInProgress}" | ||
class="w-full" | ||
inProgress="{createVolumeInProgress}" | ||
icon="{faPlusCircle}"> | ||
Create | ||
</Button> | ||
{/if} | ||
|
||
{#if createVolumeFinished} | ||
<Button on:click="{() => end()}" class="w-full">Done</Button> | ||
{/if} | ||
</div> | ||
</div> | ||
{/if} | ||
</div> | ||
</FormPage> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.