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:
Florent Benoit 2023-08-07 14:52:16 +02:00 committed by Florent BENOIT
parent 103bceab52
commit eaa8a87afd
2 changed files with 90 additions and 61 deletions

View file

@ -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();
});
});

View file

@ -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}