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: 8 additions & 7 deletions web/src/components/core/Installation.jsx → web/custom.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) [2023] SUSE LLC
* Copyright (c) [2024] SUSE LLC
*
* All Rights Reserved.
*
Expand All @@ -20,11 +20,12 @@
* find current contact information at www.suse.com.
*/

import React from "react";
import { InstallationProgress, InstallationFinished } from "~/components/core";

function Installation({ isBusy }) {
return isBusy ? <InstallationProgress /> : <InstallationFinished />;
declare module "*.svg" {
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
export default content;
}

export default Installation;
declare module "*.svg?component" {
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
export default content;
}
6 changes: 6 additions & 0 deletions web/package/agama-web-ui.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Mon Oct 28 19:26:29 UTC 2024 - David Diaz <dgonzalez@suse.com>

- Make some general actions more accessible
(gh#agama-project/agama#1690).

-------------------------------------------------------------------
Wed Oct 23 16:26:29 UTC 2024 - David Diaz <dgonzalez@suse.com>

Expand Down
46 changes: 30 additions & 16 deletions web/src/App.test.jsx → web/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) [2022-2023] SUSE LLC
* Copyright (c) [2022-2024] SUSE LLC
*
* All Rights Reserved.
*
Expand All @@ -23,10 +23,10 @@
import React from "react";
import { screen } from "@testing-library/react";
import { installerRender } from "~/test-utils";

import App from "./App";
import { createClient } from "~/client";
import { InstallationPhase } from "./types/status";
import { createClient } from "~/client";
import { Product } from "./types/software";

jest.mock("~/client");

Expand All @@ -39,9 +39,12 @@ jest.mock("~/api/l10n", () => ({
updateConfig: jest.fn(),
}));

const tumbleweed: Product = { id: "openSUSE", name: "openSUSE Tumbleweed" };
const microos: Product = { id: "Leap Micro", name: "openSUSE Micro" };

// list of available products
let mockProducts;
let mockSelectedProduct;
let mockProducts: Product[];
let mockSelectedProduct: Product;

jest.mock("~/queries/software", () => ({
...jest.requireActual("~/queries/software"),
Expand All @@ -62,6 +65,7 @@ jest.mock("~/queries/l10n", () => ({
jest.mock("~/queries/issues", () => ({
...jest.requireActual("~/queries/issues"),
useIssuesChanges: () => jest.fn(),
useAllIssues: () => ({ isEmtpy: true }),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ ⚠️ ⚠️

After moving the core/InstallButton to the top bar and using the useAllIssues to decide if it should be shown (aka mounted) or not, I have had to add this mock here and in the components/core/Header.test.tsx file to make these tests work again.

This made me ask myself if would be a good idea to have some test util for always have issues mocked and influence them when needed.

}));

jest.mock("~/queries/storage", () => ({
Expand All @@ -88,22 +92,18 @@ jest.mock("~/context/installer", () => ({
// Mock some components,
// See https://www.chakshunyu.com/blog/how-to-mock-a-react-component-in-jest/#default-export
jest.mock("~/components/questions/Questions", () => () => <div>Questions Mock</div>);
jest.mock("~/components/core/Installation", () => () => <div>Installation Mock</div>);
jest.mock("~/components/layout/Loading", () => () => <div>Loading Mock</div>);
jest.mock("~/components/product/ProductSelectionProgress", () => () => <div>Product progress</div>);

describe("App", () => {
beforeEach(() => {
// setting the language through a cookie
document.cookie = "agamaLang=en-us; path=/;";
createClient.mockImplementation(() => {
(createClient as jest.Mock).mockImplementation(() => {
return {};
});

mockProducts = [
{ id: "openSUSE", name: "openSUSE Tumbleweed" },
{ id: "Leap Micro", name: "openSUSE Micro" },
];
mockProducts = [tumbleweed, microos];
});

afterEach(() => {
Expand Down Expand Up @@ -142,7 +142,7 @@ describe("App", () => {
describe("if the service is busy", () => {
beforeEach(() => {
mockClientStatus.isBusy = true;
mockSelectedProduct = { id: "Tumbleweed" };
mockSelectedProduct = tumbleweed;
});

it("redirects to product selection progress", async () => {
Expand All @@ -163,15 +163,29 @@ describe("App", () => {
});
});

describe("on the installaiton phase", () => {
describe("on the busy installaiton phase", () => {
beforeEach(() => {
mockClientStatus.phase = InstallationPhase.Install;
mockClientStatus.isBusy = true;
mockSelectedProduct = tumbleweed;
});

it("navigates to installation progress", async () => {
installerRender(<App />, { withL10n: true });
await screen.findByText("Navigating to /installation/progress");
});
});

describe("on the idle installaiton phase", () => {
beforeEach(() => {
mockClientStatus.phase = InstallationPhase.Install;
mockSelectedProduct = { id: "Fake product" };
mockClientStatus.isBusy = false;
mockSelectedProduct = tumbleweed;
});

it("renders the application content", async () => {
it("navigates to installation finished", async () => {
installerRender(<App />, { withL10n: true });
await screen.findByText("Installation Mock");
await screen.findByText("Navigating to /installation/finished");
});
});
});
54 changes: 29 additions & 25 deletions web/src/App.jsx → web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) [2022-2023] SUSE LLC
* Copyright (c) [2022-2024] SUSE LLC
*
* All Rights Reserved.
*
Expand All @@ -22,26 +22,22 @@

import React from "react";
import { Navigate, Outlet, useLocation } from "react-router-dom";
import { Loading } from "./components/layout";
import { ServerError } from "~/components/core";
import { Loading, PlainLayout } from "~/components/layout";
import { Questions } from "~/components/questions";
import { ServerError, Installation } from "~/components/core";
import { useInstallerL10n } from "./context/installerL10n";
import { useInstallerL10n } from "~/context/installerL10n";
import { useInstallerClientStatus } from "~/context/installer";
import { useProduct, useProductChanges } from "./queries/software";
import { useProduct, useProductChanges } from "~/queries/software";
import { useL10nConfigChanges } from "~/queries/l10n";
import { useIssuesChanges } from "./queries/issues";
import { useInstallerStatus, useInstallerStatusChanges } from "./queries/status";
import { useDeprecatedChanges } from "./queries/storage";
import { PATHS as PRODUCT_PATHS } from "./routes/products";
import SimpleLayout from "./SimpleLayout";
import { InstallationPhase } from "./types/status";
import { useIssuesChanges } from "~/queries/issues";
import { useInstallerStatus, useInstallerStatusChanges } from "~/queries/status";
import { useDeprecatedChanges } from "~/queries/storage";
import { PATHS as PRODUCT_PATHS } from "~/routes/products";
import { PATHS as ROOT_PATHS } from "~/router";
import { InstallationPhase } from "~/types/status";

/**
* Main application component.
*
* @param {object} props
* @param {number} [props.max_attempts=3] - Connection attempts before displaying an
* error (3 by default). The component will keep trying to connect.
*/
function App() {
const location = useLocation();
Expand All @@ -56,25 +52,33 @@ function App() {
useDeprecatedChanges();

const Content = () => {
if (error) return <ServerError />;
if (error)
return (
<PlainLayout>
<ServerError />
</PlainLayout>
);

if (phase === InstallationPhase.Install && isBusy) {
return <Navigate to={ROOT_PATHS.installationProgress} />;
}

if (phase === InstallationPhase.Install) {
return <Installation isBusy={isBusy} />;
if (phase === InstallationPhase.Install && !isBusy) {
return <Navigate to={ROOT_PATHS.installationFinished} />;
}

if (!products || !connected)
if (!products || !connected) return <Loading />;

if (phase === InstallationPhase.Startup && isBusy) {
return (
<SimpleLayout showOutlet={false}>
<PlainLayout>
<Loading />
</SimpleLayout>
</PlainLayout>
);

if (phase === InstallationPhase.Startup && isBusy) {
return <Loading />;
}

if (selectedProduct === undefined && location.pathname !== PRODUCT_PATHS.root) {
return <Navigate to="/products" />;
return <Navigate to={PRODUCT_PATHS.root} />;
}

if (
Expand Down
62 changes: 0 additions & 62 deletions web/src/SimpleLayout.jsx

This file was deleted.

25 changes: 23 additions & 2 deletions web/src/assets/styles/patternfly-overrides.scss
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,28 @@
--pf-v5-c-button--m-secondary--Color: var(--color-link-hover);
}

.pf-v5-c-modal-box__body {
// Redefine style for primary buttons placed at top bar
.pf-v5-c-masthead__content {
.pf-v5-c-button.pf-m-primary {
--pf-v5-c-button--FontSize: 120%;
--pf-v5-c-button--m-primary--BackgroundColor: var(--color-button-primary-hover);
letter-spacing: 1px;
}

.pf-v5-c-button.pf-m-primary:hover {
--pf-v5-c-button--m-primary--BackgroundColor: #1ea064; // var(--color-button-primary);
}

.pf-v5-c-button.pf-m-primary:focus:hover {
--pf-v5-c-button--m-primary--BackgroundColor: #1ea064; // var(--color-button-primary);
}

.pf-v5-c-button.pf-m-warning {
color: var(--color-button-primary);
}
}

.pf-v5-c-button.pf-m-primary .pf-v5-c-modal-box__body {
padding-block: var(--pf-v5-c-modal-box__body--PaddingTop);
}

Expand Down Expand Up @@ -272,7 +293,7 @@

// Allows the pf-m-current directly in the a element instead of li.
// Needed because setting the pf-m-current in ReactRouter/NavLink (the one
// that knowst that link "isActive")
// that know the link "isActive")

.pf-v5-c-tabs__link.pf-m-current {
--pf-v5-c-tabs__link--after--BorderColor: var(
Expand Down
Loading