mirror of
https://github.com/podman-desktop/podman-desktop
synced 2026-05-24 10:18:53 +00:00
fix: do not use extensionInfo until it is defined
page was using extensionInfo field but this object can be undefined Add a check on the state fixes https://github.com/containers/podman-desktop/issues/3030 Signed-off-by: Florent Benoit <fbenoit@redhat.com>
This commit is contained in:
parent
103bceab52
commit
eaa8a87afd
2 changed files with 90 additions and 61 deletions
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
import '@testing-library/jest-dom';
|
||||
import { test, expect, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/svelte';
|
||||
import { render, screen, waitForElementToBeRemoved } from '@testing-library/svelte';
|
||||
import PreferencesExtensionRendering from './PreferencesExtensionRendering.svelte';
|
||||
import { extensionInfos } from '../../stores/extensions';
|
||||
|
||||
|
|
@ -113,4 +113,27 @@ describe('PreferencesExtensionRendering', () => {
|
|||
expect(remove).toBeInTheDocument();
|
||||
expect(remove).toBeDisabled();
|
||||
});
|
||||
|
||||
test('Expect empty screen if there is no matching extension (could be during providerInfos is loading)', async () => {
|
||||
// clear store
|
||||
extensionInfos.set([]);
|
||||
|
||||
// start without extension in the stores, should be empty
|
||||
render(PreferencesExtensionRendering, { extensionId: 'test' });
|
||||
|
||||
// check empty page is displayed if we do not have matching of the extension
|
||||
const emptyHeading = screen.getByRole('heading', { name: 'Extension not found', level: 1 });
|
||||
expect(emptyHeading).toBeInTheDocument();
|
||||
|
||||
// now register the extension in the store
|
||||
setup('started');
|
||||
|
||||
// wait empty page disappear
|
||||
await waitForElementToBeRemoved(() => screen.queryByRole('heading', { name: 'Extension not found', level: 1 }));
|
||||
|
||||
// now check disable button is displayed as extension is started
|
||||
const start = screen.getByRole('button', { name: 'Disable' });
|
||||
expect(start).toBeInTheDocument();
|
||||
expect(start).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@ import type { ExtensionInfo } from '../../../../main/src/plugin/api/extension-in
|
|||
import SettingsPage from './SettingsPage.svelte';
|
||||
import ExtensionStatus from '../ui/ExtensionStatus.svelte';
|
||||
import Button from '../ui/Button.svelte';
|
||||
import { faPlay, faStop, faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faPlay, faPuzzlePiece, faStop, faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||
import { router } from 'tinro';
|
||||
import EmptyScreen from '../ui/EmptyScreen.svelte';
|
||||
|
||||
export let extensionId: string = undefined;
|
||||
|
||||
let extensionInfo: ExtensionInfo;
|
||||
|
||||
$: extensionInfo = $extensionInfos.find(extension => extension.id === extensionId);
|
||||
$: hideOnboardingButton = true;
|
||||
$: hasOnboarding(extensionId).then(value => (hideOnboardingButton = value));
|
||||
|
|
@ -32,72 +34,76 @@ async function hasOnboarding(extensionId: string): Promise<boolean> {
|
|||
}
|
||||
</script>
|
||||
|
||||
<SettingsPage title="{extensionInfo.displayName} Extension">
|
||||
<span slot="subtitle">
|
||||
{extensionInfo.description}
|
||||
</span>
|
||||
<div class="flex flex-col bg-charcoal-600 rounded-md p-3">
|
||||
{#if extensionInfo}
|
||||
<Route path="/*" breadcrumb="{extensionInfo.displayName}">
|
||||
<!-- Manage lifecycle-->
|
||||
<div class="flex pb-2">
|
||||
<div class="pr-2">Status</div>
|
||||
<ExtensionStatus status="{extensionInfo.state}" />
|
||||
</div>
|
||||
|
||||
<div class="py-2 flex flex-row items-center">
|
||||
<!-- start is enabled only when stopped or failed -->
|
||||
<div class="px-2 text-sm italic text-gray-700">
|
||||
<Button
|
||||
disabled="{extensionInfo.state !== 'stopped' && extensionInfo.state !== 'failed'}"
|
||||
on:click="{() => startExtension()}"
|
||||
icon="{faPlay}">
|
||||
Enable
|
||||
</Button>
|
||||
{#if !extensionInfo}
|
||||
<EmptyScreen title="Extension not found" icon="{faPuzzlePiece}" message="No extension found with id {extensionId}" />
|
||||
{:else}
|
||||
<SettingsPage title="{extensionInfo.displayName} Extension">
|
||||
<span slot="subtitle">
|
||||
{extensionInfo.description}
|
||||
</span>
|
||||
<div class="flex flex-col bg-charcoal-600 rounded-md p-3">
|
||||
{#if extensionInfo}
|
||||
<Route path="/*" breadcrumb="{extensionInfo.displayName}">
|
||||
<!-- Manage lifecycle-->
|
||||
<div class="flex pb-2">
|
||||
<div class="pr-2">Status</div>
|
||||
<ExtensionStatus status="{extensionInfo.state}" />
|
||||
</div>
|
||||
|
||||
<!-- stop is enabled only when started -->
|
||||
<div class="px-2 text-sm italic text-gray-700">
|
||||
<Button disabled="{extensionInfo.state !== 'started'}" on:click="{() => stopExtension()}" icon="{faStop}">
|
||||
Disable
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- delete is enabled only when stopped or failed -->
|
||||
{#if extensionInfo.removable}
|
||||
<div class="py-2 flex flex-row items-center">
|
||||
<!-- start is enabled only when stopped or failed -->
|
||||
<div class="px-2 text-sm italic text-gray-700">
|
||||
<Button
|
||||
disabled="{extensionInfo.state !== 'stopped' && extensionInfo.state !== 'failed'}"
|
||||
on:click="{() => removeExtension()}"
|
||||
icon="{faTrash}">
|
||||
Remove
|
||||
on:click="{() => startExtension()}"
|
||||
icon="{faPlay}">
|
||||
Enable
|
||||
</Button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-gray-900 items-center px-2 text-sm">Default extension, cannot be removed</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="px-2 text-sm italic text-gray-700" class:hidden="{hideOnboardingButton}">
|
||||
<button
|
||||
on:click="{() => router.goto(`/preferences/onboarding/${extensionInfo.id}`)}"
|
||||
class="pf-c-button pf-m-primary"
|
||||
type="button">
|
||||
<span class="pf-c-button__icon pf-m-start">
|
||||
<i class="fas fa-play" aria-hidden="true"></i>
|
||||
</span>
|
||||
Onboarding
|
||||
</button>
|
||||
</div>
|
||||
{#if extensionInfo.error}
|
||||
<div class="flex flex-col">
|
||||
<div class="py-2">Extension error: {extensionInfo.error.message}</div>
|
||||
{#if extensionInfo.error.stack}
|
||||
<div class="py-2">Stack trace</div>
|
||||
<div class="py-2">{extensionInfo.error.stack}</div>
|
||||
<!-- stop is enabled only when started -->
|
||||
<div class="px-2 text-sm italic text-gray-700">
|
||||
<Button disabled="{extensionInfo.state !== 'started'}" on:click="{() => stopExtension()}" icon="{faStop}">
|
||||
Disable
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- delete is enabled only when stopped or failed -->
|
||||
{#if extensionInfo.removable}
|
||||
<div class="px-2 text-sm italic text-gray-700">
|
||||
<Button
|
||||
disabled="{extensionInfo.state !== 'stopped' && extensionInfo.state !== 'failed'}"
|
||||
on:click="{() => removeExtension()}"
|
||||
icon="{faTrash}">
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-gray-900 items-center px-2 text-sm">Default extension, cannot be removed</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</Route>
|
||||
{/if}
|
||||
</div></SettingsPage>
|
||||
|
||||
<div class="px-2 text-sm italic text-gray-700" class:hidden="{hideOnboardingButton}">
|
||||
<button
|
||||
on:click="{() => router.goto(`/preferences/onboarding/${extensionInfo.id}`)}"
|
||||
class="pf-c-button pf-m-primary"
|
||||
type="button">
|
||||
<span class="pf-c-button__icon pf-m-start">
|
||||
<i class="fas fa-play" aria-hidden="true"></i>
|
||||
</span>
|
||||
Onboarding
|
||||
</button>
|
||||
</div>
|
||||
{#if extensionInfo.error}
|
||||
<div class="flex flex-col">
|
||||
<div class="py-2">Extension error: {extensionInfo.error.message}</div>
|
||||
{#if extensionInfo.error.stack}
|
||||
<div class="py-2">Stack trace</div>
|
||||
<div class="py-2">{extensionInfo.error.stack}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</Route>
|
||||
{/if}
|
||||
</div></SettingsPage>
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Reference in a new issue