Skip to content

Commit 8fb5601

Browse files
authored
Merge branch 'master' into i18n
2 parents bd06d28 + 7a34d0d commit 8fb5601

File tree

11 files changed

+445
-13
lines changed

11 files changed

+445
-13
lines changed

rust/package/agama.changes

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
-------------------------------------------------------------------
2+
<<<<<<< i18n
23
Tue Mar 10 12:16:53 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>
34

45
- Recalculate the issues and actions on language change (gh#agama-project/agama#3262).
6+
=======
7+
Tue Mar 10 09:59:45 UTC 2026 - Josef Reidinger <jreidinger@suse.com>
8+
9+
- Always add hardware/filesystem/locale proposed packages when
10+
running solver (bsc#1258193)
11+
>>>>>>> master
512

613
-------------------------------------------------------------------
714
Mon Mar 9 11:37:06 UTC 2026 - Ladislav Slezák <lslezak@suse.com>

rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,9 @@ void free_patterns(const struct Patterns *patterns) noexcept;
180180
/// Representation of zypp::Product
181181
struct Product {
182182
// so far we do not need more details about the products
183-
const char *name; ///< owned
184-
const char *repo_alias; ///< owned
185-
const char *service_alias; ///< owned
183+
const char *name; ///< owned
184+
const char *repo_alias; ///< owned
185+
const char *service_alias; ///< owned
186186
};
187187

188188
struct Products {
@@ -231,7 +231,8 @@ bool is_package_selected(struct Zypp *zypp, const char *tag,
231231
bool run_solver(struct Zypp *zypp, bool only_required,
232232
struct Status *status) noexcept;
233233

234-
/// Create a solver testcase, dumps all all solver data (repositories, loaded packages...) to disk
234+
/// Create a solver testcase, dumps all all solver data (repositories, loaded
235+
/// packages...) to disk
235236
/// @param zypp see \ref init_target
236237
/// @param dir directory path where the solver testcase is saved
237238
/// @return true if the solver testcase was successfully created

rust/zypp-agama/zypp-agama-sys/c-layer/lib.cxx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,18 @@ bool run_solver(struct Zypp *zypp, bool only_required,
461461
struct Status *status) noexcept {
462462
try {
463463
STATUS_OK(status);
464-
zypp->zypp_pointer->resolver()->setOnlyRequires(only_required);
465-
return zypp->zypp_pointer->resolver()->resolvePool();
464+
auto resolver = zypp->zypp_pointer->resolver();
465+
resolver->setOnlyRequires(only_required);
466+
// needed to get hardware and locale specific provisioning
467+
// @ma: On a fresh install I'd recommend to set INR if you
468+
// want HW/Filesystem/Language supporting packages to be selected.
469+
// INR is the mode e.g. 'zypper inr' operates in, AKA InstallNewRecommends.
470+
// Recommendations of already installed packages are usually not evaluated
471+
// again. With INR they are.
472+
// The Resolver flag is setIgnoreAlreadyRecommended=0.
473+
// (addalreadyrecommended=1 is libsolv world)
474+
resolver->setIgnoreAlreadyRecommended(false);
475+
return resolver->resolvePool();
466476
} catch (zypp::Exception &excpt) {
467477
STATUS_EXCEPT(status, excpt);
468478
return false; // do not matter much as status indicate failure

web/package-lock.json

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"react-dom": "^18.3.1",
106106
"react-router": "^7.13.1",
107107
"sprintf-js": "^1.1.3",
108+
"stacktracey": "^2.1.8",
108109
"xbytes": "^1.9.1"
109110
}
110111
}

web/package/agama-web-ui.changes

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
-------------------------------------------------------------------
2+
Mon Mar 9 19:44:43 UTC 2026 - David Diaz <dgonzalez@suse.com>
3+
4+
- Improve error page with filtered, source-mapped stack traces
5+
for better error reporting (gh#agama-project/agama#3261).
6+
17
-------------------------------------------------------------------
28
Mon Mar 9 11:05:21 UTC 2026 - José Iván López González <jlopez@suse.com>
39

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright (c) [2022-2026] SUSE LLC
3+
*
4+
* All Rights Reserved.
5+
*
6+
* This program is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License as published by the Free
8+
* Software Foundation; either version 2 of the License, or (at your option)
9+
* any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14+
* more details.
15+
*
16+
* You should have received a copy of the GNU General Public License along
17+
* with this program; if not, contact SUSE LLC.
18+
*
19+
* To contact SUSE LLC about this file by physical or electronic mail, you may
20+
* find current contact information at www.suse.com.
21+
*/
22+
23+
import React from "react";
24+
import { screen } from "@testing-library/react";
25+
import { installerRender, mockRouteError } from "~/test-utils";
26+
import ErrorPage from "./ErrorPage";
27+
28+
jest.mock("stacktracey", () => jest.fn());
29+
const mockStackTracey = jest.requireMock("stacktracey");
30+
31+
const routeError = (status: number, statusText: string, data: unknown) => ({
32+
__isRouteError: true,
33+
status,
34+
statusText,
35+
data,
36+
});
37+
38+
describe("ErrorPage", () => {
39+
beforeEach(() => {
40+
mockStackTracey.mockImplementation(() => ({
41+
withSourcesAsync: jest.fn().mockResolvedValue({
42+
filter: jest.fn().mockReturnThis(),
43+
asTable: jest.fn().mockReturnValue("app.ts:10 myFunc\napp.ts:20 caller"),
44+
}),
45+
filter: jest.fn().mockReturnThis(),
46+
asTable: jest.fn().mockReturnValue("app.ts:10 myFunc\napp.ts:20 caller"),
47+
}));
48+
});
49+
describe("when the error is a route error response", () => {
50+
describe("when it is a 404", () => {
51+
beforeEach(() => {
52+
mockRouteError(routeError(404, "Not Found", null));
53+
});
54+
55+
it("shows the HTTP status and statusText", () => {
56+
installerRender(<ErrorPage />);
57+
screen.getByText("404 Not Found");
58+
});
59+
60+
it("does not show a skeleton", () => {
61+
installerRender(<ErrorPage />);
62+
expect(screen.queryByText("Retrieving error details")).not.toBeInTheDocument();
63+
});
64+
});
65+
66+
describe("when the data payload is a string", () => {
67+
beforeEach(() => {
68+
mockRouteError(routeError(403, "Forbidden", "You do not have access"));
69+
});
70+
71+
it("shows the data payload as-is", () => {
72+
installerRender(<ErrorPage />);
73+
screen.getByText("You do not have access");
74+
});
75+
});
76+
77+
describe("when the data payload is not a string", () => {
78+
beforeEach(() => {
79+
mockRouteError(
80+
routeError(422, "Unprocessable Entity", { field: "email", issue: "invalid" }),
81+
);
82+
});
83+
84+
it("shows the JSON-serialised payload", () => {
85+
installerRender(<ErrorPage />);
86+
screen.getByText(/"field":"email"/);
87+
});
88+
});
89+
});
90+
91+
describe("when the error is an unexpected error", () => {
92+
describe("when it is a standard Error", () => {
93+
beforeEach(() => {
94+
mockRouteError(new Error("Something exploded"));
95+
});
96+
97+
it("shows the 'Unexpected error' heading", async () => {
98+
installerRender(<ErrorPage />);
99+
screen.getByText("Unexpected error");
100+
await screen.findByText(/app\.ts:10.*myFunc/);
101+
});
102+
103+
it("shows the error message", async () => {
104+
installerRender(<ErrorPage />);
105+
screen.getByText("Something exploded");
106+
await screen.findByText(/app\.ts:10.*myFunc/);
107+
});
108+
109+
it("shows a skeleton while the trace is loading", async () => {
110+
installerRender(<ErrorPage />);
111+
screen.getByText("Retrieving error details");
112+
await screen.findByText(/app\.ts:10.*myFunc/);
113+
});
114+
115+
it("shows the stack trace once loaded", async () => {
116+
installerRender(<ErrorPage />);
117+
await screen.findByText(/app\.ts:10.*myFunc/);
118+
});
119+
120+
it("hides the skeleton once the trace is loaded", async () => {
121+
installerRender(<ErrorPage />);
122+
await screen.findByText(/app\.ts:10.*myFunc/);
123+
expect(screen.queryByText("Retrieving error details")).not.toBeInTheDocument();
124+
});
125+
});
126+
127+
describe("when withSourcesAsync fails", () => {
128+
beforeEach(() => {
129+
mockStackTracey.mockImplementationOnce(() => ({
130+
withSourcesAsync: jest.fn().mockRejectedValue(new Error("network error")),
131+
filter: jest.fn().mockReturnThis(),
132+
asTable: jest.fn().mockReturnValue("app.ts:10 myFunc (no sources)"),
133+
}));
134+
mockRouteError(new Error("Something exploded"));
135+
});
136+
137+
it("falls back to the raw stack table", async () => {
138+
installerRender(<ErrorPage />);
139+
await screen.findByText(/app\.ts:10.*myFunc \(no sources\)/);
140+
});
141+
});
142+
143+
describe("when the thrown value is not an Error instance", () => {
144+
beforeEach(() => {
145+
mockRouteError({ code: 42, reason: "unknown" });
146+
});
147+
148+
it("shows the 'Something went wrong' heading", async () => {
149+
installerRender(<ErrorPage />);
150+
screen.getByText("Something went wrong");
151+
await screen.findByText(/"code":42/);
152+
});
153+
154+
it("shows 'Unknown error' as the message", async () => {
155+
installerRender(<ErrorPage />);
156+
screen.getByText("Unknown error");
157+
await screen.findByText(/"code":42/);
158+
});
159+
160+
it("shows the JSON-serialised value", async () => {
161+
installerRender(<ErrorPage />);
162+
await screen.findByText(/"code":42/);
163+
});
164+
});
165+
});
166+
});

0 commit comments

Comments
 (0)