Skip to content

Commit

Permalink
docs: adding dev-docs for adding a new page to the installer (#884)
Browse files Browse the repository at this point in the history
Based on my learnings from stubbing out landscape integratino in #882, I
have put together some `dev-docs` outlining the broad steps required for
adding a new page to the insatller, as well as adding some doc comments
on some relevant dart components that did not have any.
  • Loading branch information
matthew-hagemann authored Dec 5, 2024
2 parents e0a1532 + ed5c4a0 commit ccf8ba2
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 1 deletion.
15 changes: 14 additions & 1 deletion apps/ubuntu_bootstrap/lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ import 'package:yaru/yaru.dart';

export 'app/installer_wizard.dart';

/// Runs the installer.
///
/// This function is the main entrypoint to the installer. It parses command-line arguments,
/// forwards arguments to subiquity, initializes the backend services used by the different
/// installation steps and starts the UI.
///
/// Command-Line Options:
/// - `--dry-run`: Runs the Subiquity server in dry-run mode.
/// - `--dry-run-config`: Path to the configuration file for dry-run mode.
/// - `--machine-config`: Path to the machine configuration file for dry-run mode.
/// - `--source-catalog`: Path to the source catalog file for dry-run mode.
/// - `--try-or-install`: Displays the "Try or Install" page on startup.
Future<void> runInstallerApp(
List<String> args, {
ThemeData? theme,
Expand Down Expand Up @@ -79,7 +91,8 @@ Future<void> runInstallerApp(
.then((dir) => Directory(dir).existsSync() ? dir : null),
);

// conditional registration if not already registered by flavors or tests
// Conditional registration if not already registered by flavors or tests. All services must be
// registered here or their respective providers will fail to find them when building models.
tryRegisterService<AccessibilityService>(GnomeAccessibilityService.new);
tryRegisterService<ActiveDirectoryService>(
() => SubiquityActiveDirectoryService(getService<SubiquityClient>()),
Expand Down
20 changes: 20 additions & 0 deletions apps/ubuntu_bootstrap/lib/app/installation_step.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@ import 'package:ubuntu_provision/ubuntu_provision.dart';
import 'package:ubuntu_utils/ubuntu_utils.dart';
import 'package:ubuntu_wizard/ubuntu_wizard.dart';

/// Represents each step or page in the installer workflow.
///
/// Each step corresponds to an enum value, which defines the page's behavior
/// and its role in the installer. The order of the enums determines the sequence
/// in which the steps appear in the installer.
///
/// New pages must be added as additional enums to integrate into the workflow.
///
/// Properties:
/// - `discreteStep`: Indicates whether the step is displayed as a discrete wizard step. Discrete
/// steps get their own circle progress indicator at the bottom of the window.
/// - `wizardStep`: Specifies if the step is part of the wizard flow. Wizard steps appear with
/// circle progress indicators and the default step navigation at the bottom of the window.
/// - `required`: Marks the step as mandatory in the workflow.
/// - `allowedToHide`: Determines if the step can be conditionally hidden.
///
/// References:
/// - [A discrete, wizard step](https://github.com/canonical/ubuntu-desktop-provision-screenshots/blob/main/bootstrap/light/accessibility.png)
/// - [A non-discrete, wizard step](https://github.com/canonical/ubuntu-desktop-provision-screenshots/blob/main/bootstrap/light/rst.png)
/// - [A non-discrete, non-wizard step](https://github.com/canonical/ubuntu-desktop-provision-screenshots/blob/main/bootstrap/light/install-0.png)
enum InstallationStep with RouteName {
loading(
LoadingPage.new,
Expand Down
67 changes: 67 additions & 0 deletions dev-docs/adding-a-new-page-to-the-installer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Adding a new page to the installer

This project makes use of the [Model-view-viewmodel](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel)
architectural pattern.

## Add your page
New pages can either be added to `apps/ubuntu_bootstrap/` for the main installer, `apps/ubuntu_init/`
for the first boot experience, or `packages/ubuntu_provision/` if they need to be shared between
the two flows. This document will assume you are adding a page to the main installer.

- Under `apps/ubuntu_bootstrap/lib/pages`, create a new directory for the page you would like to add
ie: `apps/ubuntu_bootstrap/lib/pages/<your-feature>`.
- Create at minimum the following files (see existing pages for what these files should include):
- `<your-feature>_page.dart`: Defines the UI for the page you would like to add to the installer.
This forms the `view` within the MVVM pattern.
- `<your-feature>_model.dart`: Implements the provider and integrates with backend services to
support your page's functionality. This forms the `view-model` within the MVVM pattern.
- Export your page for use in the installer by adding it to `apps/ubuntu_bootstrap/lib/pages.dart`
- Add your page to the installation steps by adding it as an enum to `InstallationStep` in
`apps/ubuntu_bootstrap/lib/app/installation_step.dart`. The order of the `InstallationStep` enums
determines the order they appear in the installation process.

Not all wizard pages are standalone. For more complicated flows, instead of adding a page, an
embedded wizard can be added to `InstallationStep`. These embedded wizards house their own wizard
steps, and may share state between multiple pages. See `StorageWizard` in
`apps/ubuntu_bootstrap/lib/pages/storage/storage_wizard.dart`

## Add your service
- Under `apps/ubuntu_bootstrap/lib/services`, create a new file `<your-feature>_service.dart`
- Within this file, define the backend service that supports your page, and will be called to by
your model. This forms the `model` within the MVVM pattern.
- Export your service for use by your model by adding it to
`apps/ubuntu_bootstrap/lib/services.dart`
- Register your service for use in the installer by adding a statement
`tryRegisterService<your-service>()` to `apps/ubuntu_bootstrap/lib/app.dart`.

## Add UI text to the l10n file for translation
- Add any text that appear to the user on your page to the
`apps/ubuntu_bootstrap/lib/l10n/ubuntu_bootstrap_en.arb` file.
- In the `ubuntu_bootstrap` project root, run `flutter gen-l10n` to generate getters for use in your
page.
- Import `apps/ubuntu_bootstrap/lib/l10n/ubuntu_bootstrap_localizations.dart` and make use of the
variables you defined in `ubuntu_bootstrap_en.arb`. This allows for appropriate translated strings
to be chosen based on the end users selected locale.

## Update tests
### `installer_wizard_test.dart`
- Under `test/`, add a directory for your new page and create at minimum a `build<your-feature>Model`
helper function that returns a mocked model, making use of the `mokito` annotations for mock generation.
- Run `melos generate` to generate the mocks.
- For each failing test in `test/installer_wizard_test.dart`
- Initialize your mocked model
- Add the initialized model to the to the `ProviderScope` overrides.
- If needed, add statements to correctly navigate and identify your new page in the test flow,
using `tapNext()`, `pumpAndSettle()` and `expect()`.

From here, appropriate unit tests for the new page should be added under the newly created
`test/<your-feature>` directory.

### Integration tests
The integration tests are based on the absolute order in which pages appear in `InstallationStep`
in the `apps/ubuntu_bootstrap/lib/app/installation_step.dart` file. Adding a new page will thus
break all integration tests.

To fix this, add a new `Future<void> test<your-feature>Page()` function to
`UbuntuBootstrapPageTester` in the `/packages/ubuntu_provision_test/lib/src/bootstrap_tester.dart`
file. Adding this function in order to each integration test should fix the failing tests.

0 comments on commit ccf8ba2

Please sign in to comment.