Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/frontend/src/lib/forms/compose/QuadletComposeForm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ import { readable } from 'svelte/store';
import type { ProviderApi } from '/@shared/src/apis/provide-api';
import type { PodletApi } from '/@shared/src/apis/podlet-api';
import type { QuadletApi } from '/@shared/src/apis/quadlet-api';
import { router } from 'tinro';

// mock router lib
vi.mock(import('tinro'));

// mock clients
vi.mock(import('/@/api/client'), () => ({
Expand Down Expand Up @@ -67,6 +71,17 @@ beforeEach(() => {
});

describe('step select', () => {
test('expect container engine to be automatically selected', async () => {
render(QuadletComposeForm, {
providerId: undefined,
connection: undefined,
loading: false,
});

expect(router.location.query.set).toHaveBeenCalledWith('providerId', WSL_PROVIDER_DETAILED_INFO.providerId);
expect(router.location.query.set).toHaveBeenCalledWith('connection', WSL_PROVIDER_DETAILED_INFO.name);
});

test('file provided as parameter should be displayed', async () => {
const { getByRole } = render(QuadletComposeForm, {
filepath: FILEPATH_MOCK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ let selectedContainerProviderConnection: ProviderContainerConnectionDetailedInfo
$providerConnectionsInfo.find(provider => provider.providerId === providerId && provider.name === connection),
);

$effect(() => {
if (!selectedContainerProviderConnection && $providerConnectionsInfo.length > 0) {
onContainerProviderConnectionChange($providerConnectionsInfo[0]);
}
});

const DEFAULT_KUBE_QUADLET = `
[Unit]
Description=A kubernetes yaml based service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ import type { Component, ComponentProps } from 'svelte';
import type { ContainerApi } from '/@shared/src/apis/container-api';
import type { ProviderApi } from '/@shared/src/apis/provide-api';
import type { PodletApi } from '/@shared/src/apis/podlet-api';
import { router } from 'tinro';

// mock router lib
vi.mock(import('tinro'));

// mock clients
vi.mock(import('/@/api/client'), () => ({
Expand Down Expand Up @@ -67,6 +71,18 @@ beforeEach(() => {
});

describe('Step options', () => {
test('expect container engine to be automatically selected', async () => {
render(QuadletGenerateForm, {
providerId: undefined,
connection: undefined,
loading: false,
close: vi.fn(),
});

expect(router.location.query.set).toHaveBeenCalledWith('providerId', WSL_PROVIDER_DETAILED_INFO.providerId);
expect(router.location.query.set).toHaveBeenCalledWith('connection', WSL_PROVIDER_DETAILED_INFO.name);
});

test('expect cancel to call close', async () => {
const closeMock = vi.fn();
const { getByRole } = render(QuadletGenerateForm, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ let selectedContainerProviderConnection: ProviderContainerConnectionDetailedInfo
$providerConnectionsInfo.find(provider => provider.providerId === providerId && provider.name === connection),
);

$effect(() => {
if (!selectedContainerProviderConnection && $providerConnectionsInfo.length > 0) {
onContainerProviderConnectionChange($providerConnectionsInfo[0]);
}
});

function onQuadletTypeChange(value: string): void {
router.location.query.set('quadletType', value);
router.location.query.delete(RESOURCE_ID_QUERY); // delete the key
Expand Down Expand Up @@ -174,6 +180,7 @@ function resetGenerate(): void {
disabled={loading}
onChange={onContainerProviderConnectionChange}
value={selectedContainerProviderConnection}
clearable={false}
containerProviderConnections={$providerConnectionsInfo} />
{#if selectedContainerProviderConnection && selectedContainerProviderConnection.status !== 'started'}
<div class="text-gray-800 text-sm flex items-center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
***********************************************************************/

import '@testing-library/jest-dom/vitest';
import { beforeEach, expect, test, vi } from 'vitest';
import { beforeEach, expect, test, vi, describe } from 'vitest';
import { render, within } from '@testing-library/svelte';
import ContainerProviderConnectionSelect from '/@/lib/select/ContainerProviderConnectionSelect.svelte';
import { VMType } from '/@shared/src/utils/vm-types';
Expand Down Expand Up @@ -71,3 +71,28 @@ test('default value should be visible', async () => {
const select = within(container).getByText(qemuConnection.name);
expect(select).toBeDefined();
});

describe('clear button', () => {
test('clear button should be visible by default', async () => {
const { container } = render(ContainerProviderConnectionSelect, {
value: qemuConnection,
containerProviderConnections: [wslConnection, qemuConnection],
});

// find clear HTMLElement
const clear = container.querySelector('button[class~="clear-select"]');
expect(clear).toBeDefined();
});

test('clearable prop should be propagated to Select component', async () => {
const { container } = render(ContainerProviderConnectionSelect, {
value: qemuConnection,
containerProviderConnections: [wslConnection, qemuConnection],
clearable: false,
});

// find clear HTMLElement
const clear = container.querySelector('button[class~="clear-select"]');
expect(clear).toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ interface Props {
onChange?: (value: ProviderContainerConnectionDetailedInfo | undefined) => void;
containerProviderConnections: ProviderContainerConnectionDetailedInfo[];
disabled?: boolean;
clearable?: boolean;
}

let { value = $bindable(), containerProviderConnections, onChange, disabled }: Props = $props();
let { value = $bindable(), clearable = true, containerProviderConnections, onChange, disabled }: Props = $props();

/**
* Handy mechanism to provide the mandatory property `label` and `value` to the Select component
Expand Down Expand Up @@ -47,6 +48,7 @@ function getProviderStatusColor(item: ProviderContainerConnectionDetailedInfo):
disabled={disabled}
value={selected}
onchange={handleOnChange}
clearable={clearable}
placeholder="Select container provider to use"
items={containerProviderConnections.map(containerProviderConnection => ({
...containerProviderConnection,
Expand Down
79 changes: 51 additions & 28 deletions packages/frontend/src/lib/select/Select.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
* Copyright (C) 2024-2025 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.
Expand All @@ -17,7 +17,7 @@
***********************************************************************/

import '@testing-library/jest-dom/vitest';
import { beforeEach, vi, test, expect } from 'vitest';
import { beforeEach, vi, test, expect, describe } from 'vitest';
import { render, fireEvent, within } from '@testing-library/svelte';
import Select from '/@/lib/select/Select.svelte';

Expand Down Expand Up @@ -122,37 +122,60 @@ test('selecting value should call onchange callback', async () => {
});
});

test('clearing value should call onchange callback with undefined', async () => {
const onChangeMock = vi.fn();
const { container } = render(Select, {
label: 'Select Item',
items: [
{
label: 'Dummy Item 1',
value: 'item-1',
},
{
describe('clear button', () => {
test('clearing value should call onchange callback with undefined', async () => {
const onChangeMock = vi.fn();
const { container } = render(Select, {
label: 'Select Item',
items: [
{
label: 'Dummy Item 1',
value: 'item-1',
},
{
label: 'Dummy Item 2',
value: 'item-2',
},
],
value: {
label: 'Dummy Item 2',
value: 'item-2',
},
],
value: {
label: 'Dummy Item 2',
value: 'item-2',
},
onchange: onChangeMock,
});
onchange: onChangeMock,
});

// get clear HTMLElement
const clear = container.querySelector('button[class~="clear-select"]');
// ensure we have two options
expect(clear).not.toBeNull();
if (!clear) throw new Error('clear is null');
// get clear HTMLElement
const clear = container.querySelector('button[class~="clear-select"]');
// ensure we have two options
expect(clear).not.toBeNull();
if (!clear) throw new Error('clear is null');

await fireEvent.click(clear);
await fireEvent.click(clear);

await vi.waitFor(() => {
expect(onChangeMock).toHaveBeenCalledWith(undefined);
expect(onChangeMock).toHaveBeenCalledOnce();
await vi.waitFor(() => {
expect(onChangeMock).toHaveBeenCalledWith(undefined);
expect(onChangeMock).toHaveBeenCalledOnce();
});
});

test('clearable props should be respected', async () => {
const { container } = render(Select, {
label: 'Select Item',
items: [
{
label: 'Dummy Item 1',
value: 'item-1',
},
],
value: {
label: 'Dummy Item 1',
value: 'item-1',
},
clearable: false,
});

// find clear HTMLElement
const clear = container.querySelector('button[class~="clear-select"]');
expect(clear).toBeNull();
});
});
2 changes: 2 additions & 0 deletions packages/frontend/src/lib/select/Select.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export let placeholder: string | undefined = undefined;
export let label: string | undefined = undefined;
export let name: string | undefined = undefined;
export let onchange: ((value: T | undefined) => void) | undefined = undefined;
export let clearable: boolean = true;

function handleOnChange(e: CustomEvent<T | undefined>): void {
value = e.detail;
Expand Down Expand Up @@ -52,6 +53,7 @@ function handleOnClear(): void {
--height="32px"
--max-height="32px"
placeholder={placeholder}
clearable={clearable}
class="!bg-[var(--pd-content-bg)] !text-[var(--pd-content-card-text)]"
items={items}
showChevron={!disabled}>
Expand Down
12 changes: 0 additions & 12 deletions tests/playwright/src/quadlet-extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,6 @@ test.describe.serial(`Podman Quadlet extension installation and verification`, {
await playExpect(generateForm.cancelButton).toBeEnabled();
await playExpect(generateForm.generateButton).toBeDisabled(); // default should be disabled

// open the select dropdown
const podmanProviders = await generateForm.containerEngineSelect.getOptions();
playExpect(podmanProviders.length).toBeGreaterThan(0);

const sorted = podmanProviders.find(provider => provider.toLowerCase().includes('podman'));
if (!sorted) throw new Error('cannot found podman provider');

// Value can be `podman-machine-default (WSL)`
const machine = sorted.split(' ')[0];
console.log(`Trying to use provider ${machine}`);
await generateForm.containerEngineSelect.set(machine);

// wait for loading to be finished
await playExpect
.poll(async () => await generateForm.isLoading(), {
Expand Down