From 7fccfb5738d975ee79ff527fa86049e2e3462b7b Mon Sep 17 00:00:00 2001 From: Stefano Cunego <93382903+kukkok3@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:59:42 +0100 Subject: [PATCH 1/5] chore(general): update ci to v1.5.3 (#1527) * chore: update ci to v1.5.2 * chore: bumps ci to version 1.5.3 --- .github/workflows/ci.yml | 2 +- .../flutter-uikit-example-firebase-deploy.yml | 6 +++--- .github/workflows/generate-allure-report.yml | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b466060043..bcf6b159156 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ permissions: jobs: ci: - uses: input-output-hk/catalyst-forge/.github/workflows/ci.yml@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/.github/workflows/ci.yml@ci/v1.5.3 with: forge_version: 0.8.0 diff --git a/.github/workflows/flutter-uikit-example-firebase-deploy.yml b/.github/workflows/flutter-uikit-example-firebase-deploy.yml index bd2bdcaec6a..7fef0e1dc35 100644 --- a/.github/workflows/flutter-uikit-example-firebase-deploy.yml +++ b/.github/workflows/flutter-uikit-example-firebase-deploy.yml @@ -20,13 +20,13 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Forge - uses: input-output-hk/catalyst-forge/actions/install@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/actions/install@ci/v1.5.3 with: version: 0.8.0 - name: Setup CI - uses: input-output-hk/catalyst-forge/actions/setup@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/actions/setup@ci/v1.5.3 - name: Build Flutter Web - uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.3 if: always() continue-on-error: true with: diff --git a/.github/workflows/generate-allure-report.yml b/.github/workflows/generate-allure-report.yml index 3195187d94f..4751151aaff 100644 --- a/.github/workflows/generate-allure-report.yml +++ b/.github/workflows/generate-allure-report.yml @@ -26,16 +26,16 @@ jobs: - uses: actions/checkout@v4 - name: Install Forge - uses: input-output-hk/catalyst-forge/actions/install@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/actions/install@ci/v1.5.3 with: version: 0.8.0 if: always() - name: Setup CI - uses: input-output-hk/catalyst-forge/actions/setup@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/actions/setup@ci/v1.5.3 - name: Get catalyst gateway unit test report - uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.3 if: always() continue-on-error: true with: @@ -43,7 +43,7 @@ jobs: args: ./catalyst-gateway+build - name: Get schemathesis test report - uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.3 if: always() continue-on-error: true with: @@ -51,7 +51,7 @@ jobs: args: ./catalyst-gateway/tests/schemathesis_tests+test-fuzzer-api - name: Get flutter unit test report - uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.3 if: always() continue-on-error: true with: @@ -59,7 +59,7 @@ jobs: args: ./catalyst_voices+test-unit - name: Get python api test report - uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.1 + uses: input-output-hk/catalyst-forge/actions/run@ci/v1.5.3 if: always() continue-on-error: true with: From 21263b1b50e2125abb111a0043d951c8fe80f1a9 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj <38898766+apskhem@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:57:25 +0700 Subject: [PATCH 2/5] feat(cat-gateway): Add memory metrics to improve observability (#1499) * refactor(cat-gateway): Restructure cat-gateway metrics module (#1483) * feat: initial files * refactor: new structure * feat: initial crate * fix: mem deps * feat: memory tracker * feat: error handling * feat: register module * fix: region * chore: fmtfix * chore: lintfix * feat: is initialized * chore: cspell * refactor: rw * feat: memory metrics envar * fix: cspell * chore: fmtfix * feat: metrics * chore: final cleanup --- catalyst-gateway/bin/Cargo.toml | 2 + catalyst-gateway/bin/src/metrics/memory.rs | 166 ++++++++++++++++++ catalyst-gateway/bin/src/metrics/mod.rs | 2 + catalyst-gateway/bin/src/settings/mod.rs | 34 ++-- .../bin/src/settings/str_env_var.rs | 38 ++++ 5 files changed, 227 insertions(+), 15 deletions(-) diff --git a/catalyst-gateway/bin/Cargo.toml b/catalyst-gateway/bin/Cargo.toml index f55833967ad..565f0ed396c 100644 --- a/catalyst-gateway/bin/Cargo.toml +++ b/catalyst-gateway/bin/Cargo.toml @@ -98,6 +98,8 @@ regex = "1.11.1" minijinja = "2.5.0" bytes = "1.9.0" mime = "0.3.17" +stats_alloc = "0.1.10" +memory-stats = "1.0.0" [dev-dependencies] proptest = "1.5.0" diff --git a/catalyst-gateway/bin/src/metrics/memory.rs b/catalyst-gateway/bin/src/metrics/memory.rs index 7da8bd333e9..13874d98744 100644 --- a/catalyst-gateway/bin/src/metrics/memory.rs +++ b/catalyst-gateway/bin/src/metrics/memory.rs @@ -1 +1,167 @@ //! Metrics related to memory analytics. + +use std::{ + alloc::System, + sync::atomic::{AtomicBool, Ordering}, + thread, +}; + +use memory_stats::{memory_stats, MemoryStats}; +use stats_alloc::{Region, StatsAlloc, INSTRUMENTED_SYSTEM}; + +use crate::settings::Settings; + +/// Use the instrumented allocator for gathering allocation statistics. +/// Note: This wraps the global allocator. +/// All structs that use the global allocator can be tracked. +#[global_allocator] +static GLOBAL: &StatsAlloc = &INSTRUMENTED_SYSTEM; + +/// This is to prevent the init function from accidentally being called multiple times. +static IS_INITIALIZED: AtomicBool = AtomicBool::new(false); + +/// Starts a background thread to periodically update memory metrics. +/// +/// This function spawns a thread that updates the global `MemoryMetrics` +/// structure at regular intervals defined by `UPDATE_INTERVAL_MILLI`. +pub(crate) fn init_metrics_updater() { + if IS_INITIALIZED.swap(true, Ordering::SeqCst) { + return; + } + + let stats = Region::new(GLOBAL); + let api_host_names = Settings::api_host_names().join(","); + let service_id = Settings::service_id(); + + thread::spawn(move || { + loop { + { + let allocator_stats = stats.change(); + let mem_stats = memory_stats().unwrap_or({ + MemoryStats { + physical_mem: 0, + virtual_mem: 0, + } + }); + + reporter::MEMORY_PHYSICAL_USAGE + .with_label_values(&[&api_host_names, service_id]) + .set(i64::try_from(mem_stats.physical_mem).unwrap_or(-1)); + reporter::MEMORY_VIRTUAL_USAGE + .with_label_values(&[&api_host_names, service_id]) + .set(i64::try_from(mem_stats.virtual_mem).unwrap_or(-1)); + reporter::MEMORY_ALLOCATION_COUNT + .with_label_values(&[&api_host_names, service_id]) + .set(i64::try_from(allocator_stats.allocations).unwrap_or(-1)); + reporter::MEMORY_DEALLOCATION_COUNT + .with_label_values(&[&api_host_names, service_id]) + .set(i64::try_from(allocator_stats.deallocations).unwrap_or(-1)); + reporter::MEMORY_REALLOCATION_COUNT + .with_label_values(&[&api_host_names, service_id]) + .set(i64::try_from(allocator_stats.reallocations).unwrap_or(-1)); + reporter::MEMORY_BYTES_ALLOCATED + .with_label_values(&[&api_host_names, service_id]) + .set(i64::try_from(allocator_stats.bytes_allocated).unwrap_or(-1)); + reporter::MEMORY_BYTES_DEALLOCATED + .with_label_values(&[&api_host_names, service_id]) + .set(i64::try_from(allocator_stats.bytes_deallocated).unwrap_or(-1)); + reporter::MEMORY_BYTES_REALLOCATED + .with_label_values(&[&api_host_names, service_id]) + .set(i64::try_from(allocator_stats.bytes_reallocated).unwrap_or(-1)); + } + + thread::sleep(Settings::metrics_memory_interval()); + } + }); +} + +/// All the related memory reporting metrics to the Prometheus service are inside this +/// module. +mod reporter { + use std::sync::LazyLock; + + use prometheus::{register_int_gauge_vec, IntGaugeVec}; + + /// Labels for the client metrics + const MEMORY_METRIC_LABELS: [&str; 2] = ["api_host_names", "service_id"]; + + /// The "physical" memory used by this process, in bytes. + pub(super) static MEMORY_PHYSICAL_USAGE: LazyLock = LazyLock::new(|| { + register_int_gauge_vec!( + "memory_physical_usage", + "Amount of physical memory usage in bytes", + &MEMORY_METRIC_LABELS + ) + .unwrap() + }); + + /// The "virtual" memory used by this process, in bytes. + pub(super) static MEMORY_VIRTUAL_USAGE: LazyLock = LazyLock::new(|| { + register_int_gauge_vec!( + "memory_virtual_usage", + "Amount of physical virtual usage in bytes", + &MEMORY_METRIC_LABELS + ) + .unwrap() + }); + + /// The number of allocation count in the heap. + pub(super) static MEMORY_ALLOCATION_COUNT: LazyLock = LazyLock::new(|| { + register_int_gauge_vec!( + "memory_allocation_count", + "Number of allocation count in the heap", + &MEMORY_METRIC_LABELS + ) + .unwrap() + }); + + /// The number of deallocation count in the heap. + pub(super) static MEMORY_DEALLOCATION_COUNT: LazyLock = LazyLock::new(|| { + register_int_gauge_vec!( + "memory_deallocation_count", + "Number of deallocation count in the heap", + &MEMORY_METRIC_LABELS + ) + .unwrap() + }); + + /// The number of reallocation count in the heap. + pub(super) static MEMORY_REALLOCATION_COUNT: LazyLock = LazyLock::new(|| { + register_int_gauge_vec!( + "memory_reallocation_count", + "Number of reallocation count in the heap", + &MEMORY_METRIC_LABELS + ) + .unwrap() + }); + + /// The amount of accumulative allocated bytes in the heap. + pub(super) static MEMORY_BYTES_ALLOCATED: LazyLock = LazyLock::new(|| { + register_int_gauge_vec!( + "memory_bytes_allocated", + "Amount of accumulative allocated bytes in the heap", + &MEMORY_METRIC_LABELS + ) + .unwrap() + }); + + /// The amount of accumulative deallocated bytes in the heap. + pub(super) static MEMORY_BYTES_DEALLOCATED: LazyLock = LazyLock::new(|| { + register_int_gauge_vec!( + "memory_bytes_deallocated", + "Amount of accumulative deallocated bytes in the heap", + &MEMORY_METRIC_LABELS + ) + .unwrap() + }); + + /// The amount of accumulative reallocated bytes in the heap. + pub(super) static MEMORY_BYTES_REALLOCATED: LazyLock = LazyLock::new(|| { + register_int_gauge_vec!( + "memory_bytes_reallocated", + "Amount of accumulative reallocated bytes in the heap", + &MEMORY_METRIC_LABELS + ) + .unwrap() + }); +} diff --git a/catalyst-gateway/bin/src/metrics/mod.rs b/catalyst-gateway/bin/src/metrics/mod.rs index ba8d71503da..74725efee27 100644 --- a/catalyst-gateway/bin/src/metrics/mod.rs +++ b/catalyst-gateway/bin/src/metrics/mod.rs @@ -14,5 +14,7 @@ pub(crate) mod memory; /// Returns the default prometheus registry. #[must_use] pub(crate) fn init_prometheus() -> Registry { + memory::init_metrics_updater(); + default_registry().clone() } diff --git a/catalyst-gateway/bin/src/settings/mod.rs b/catalyst-gateway/bin/src/settings/mod.rs index 6be03945050..d3702d39373 100644 --- a/catalyst-gateway/bin/src/settings/mod.rs +++ b/catalyst-gateway/bin/src/settings/mod.rs @@ -11,7 +11,6 @@ use anyhow::anyhow; use cardano_chain_follower::Network; use clap::Args; use dotenvy::dotenv; -use duration_string::DurationString; use str_env_var::StringEnvVar; use tracing::error; use url::Url; @@ -52,6 +51,9 @@ const API_URL_PREFIX_DEFAULT: &str = "/api"; /// Default `CHECK_CONFIG_TICK` used in development. const CHECK_CONFIG_TICK_DEFAULT: &str = "5s"; +/// Default `METRICS_MEMORY_INTERVAL`. +const METRICS_MEMORY_INTERVAL_DEFAULT: &str = "1s"; + /// Default Event DB URL. const EVENT_DB_URL_DEFAULT: &str = "postgresql://postgres:postgres@localhost/catalyst_events?sslmode=disable"; @@ -144,6 +146,9 @@ struct EnvVars { /// Tick every N seconds until config exists in db #[allow(unused)] check_config_tick: Duration, + + /// Interval for updating and sending memory metrics. + metrics_memory_interval: Duration, } // Lazy initialization of all env vars which are not command line parameters. @@ -157,19 +162,6 @@ static ENV_VARS: LazyLock = LazyLock::new(|| { // Support env vars in a `.env` file, doesn't need to exist. dotenv().ok(); - let check_interval = StringEnvVar::new("CHECK_CONFIG_TICK", CHECK_CONFIG_TICK_DEFAULT.into()); - let check_config_tick = match DurationString::try_from(check_interval.as_string()) { - Ok(duration) => duration.into(), - Err(error) => { - error!( - "Invalid Check Config Tick Duration: {} : {}. Defaulting to 5 seconds.", - check_interval.as_str(), - error - ); - Duration::from_secs(5) - }, - }; - EnvVars { github_repo_owner: StringEnvVar::new("GITHUB_REPO_OWNER", GITHUB_REPO_OWNER_DEFAULT.into()), github_repo_name: StringEnvVar::new("GITHUB_REPO_NAME", GITHUB_REPO_NAME_DEFAULT.into()), @@ -194,7 +186,14 @@ static ENV_VARS: LazyLock = LazyLock::new(|| { ), chain_follower: chain_follower::EnvVars::new(), internal_api_key: StringEnvVar::new_optional("INTERNAL_API_KEY", true), - check_config_tick, + check_config_tick: StringEnvVar::new_as_duration( + "CHECK_CONFIG_TICK", + CHECK_CONFIG_TICK_DEFAULT, + ), + metrics_memory_interval: StringEnvVar::new_as_duration( + "METRICS_MEMORY_INTERVAL", + METRICS_MEMORY_INTERVAL_DEFAULT, + ), } }); @@ -288,6 +287,11 @@ impl Settings { ENV_VARS.service_id.as_str() } + /// The memory metrics interval + pub(crate) fn metrics_memory_interval() -> Duration { + ENV_VARS.metrics_memory_interval + } + /// Get a list of all host names to serve the API on. /// /// Used by the `OpenAPI` Documentation to point to the correct backend. diff --git a/catalyst-gateway/bin/src/settings/str_env_var.rs b/catalyst-gateway/bin/src/settings/str_env_var.rs index 18e8a6414b7..4676e29eb93 100644 --- a/catalyst-gateway/bin/src/settings/str_env_var.rs +++ b/catalyst-gateway/bin/src/settings/str_env_var.rs @@ -1,10 +1,15 @@ //! Processing for String Environment Variables + +// cspell: words smhdwy + use std::{ env::{self, VarError}, fmt::{self, Display}, str::FromStr, + time::Duration, }; +use duration_string::DurationString; use strum::VariantNames; use tracing::{error, info}; @@ -168,6 +173,39 @@ impl StringEnvVar { value } + /// Convert an Envvar into the required Duration type. + pub(crate) fn new_as_duration(var_name: &str, default: &str) -> Duration { + let choices = "A value in the format of `[0-9]+(ns|us|ms|[smhdwy])`"; + + let raw_value = StringEnvVar::new( + var_name, + (default.to_string().as_str(), false, choices).into(), + ) + .as_string(); + + match DurationString::try_from(raw_value.clone()) { + Ok(duration) => duration.into(), + Err(error) => { + error!( + "Invalid Duration: {} : {}. Defaulting to {}.", + raw_value, error, default + ); + + match DurationString::try_from(default.to_string()) { + Ok(duration) => duration.into(), + // The error from parsing the default value must not happen + Err(error) => { + error!( + "Invalid Default Duration: {} : {}. Defaulting to 1s.", + default, error + ); + Duration::from_secs(1) + }, + } + }, + } + } + /// Convert an Envvar into an integer in the bounded range. pub(super) fn new_as(var_name: &str, default: T, min: T, max: T) -> T where From 56454b6468e42b37c53b5f509ee410b202085136 Mon Sep 17 00:00:00 2001 From: Stefano Cunego <93382903+kukkok3@users.noreply.github.com> Date: Thu, 16 Jan 2025 07:17:09 +0100 Subject: [PATCH 3/5] test(cat-gateway): adds nightly schemathesis (#1504) * wip * wip * wip * wip * wip * wip * fix: remove scylla earthfile * feat: runs schemathesis only on api/started endpoint * clean up * fix: blueprint * fix: blueprint * fix: blueprint * fix: blueprint * fix: remove hooks * fix: remove some checks * feat: adds seed param to chemathesis package * fix: poetry * fix: earthly dockerd bug * fix: remove checks all param * fix: remove duplicated function * fix: regex * adds all check * chore: clean up * clean up * chore: update schemathesis version * fix: update schemathesis option to latest version * fixes * fix: st target failing statment * feat: adds schemathesis test target * test run * test run * remove test values --------- Co-authored-by: Steven Johnson Co-authored-by: Oleksandr Prokhorenko --- .../tests/schemathesis_tests/Earthfile | 74 +++++++++++++++++-- .../tests/schemathesis_tests/blueprint.cue | 1 + 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/catalyst-gateway/tests/schemathesis_tests/Earthfile b/catalyst-gateway/tests/schemathesis_tests/Earthfile index ac66d900f4f..f5cdb0f9a4c 100644 --- a/catalyst-gateway/tests/schemathesis_tests/Earthfile +++ b/catalyst-gateway/tests/schemathesis_tests/Earthfile @@ -3,9 +3,8 @@ VERSION 0.8 package-schemathesis: FROM python:3.12-alpine3.20 # TODO: https://github.com/input-output-hk/catalyst-voices/issues/465 - ARG openapi_spec - # optional argument that can be used to pass a --hypothesis-seed to replicate specific test failures - ARG seed + ARG api_spec + ARG seed # optional argument that can be used to pass a --hypothesis-seed to replicate specific test failures ARG version=3.39.5 RUN apk add --no-cache gcc musl-dev @@ -13,9 +12,10 @@ package-schemathesis: RUN mkdir /results COPY ./hooks/hooks.py . VOLUME /results - ENTRYPOINT st run --include-path-regex '^/api/v1/health/' \ + + ENTRYPOINT st run --exclude-path-regex 'draft' \ --exclude-path '/api/v1/health/inspection' \ #excluding since this is a internal debug endpoint - $openapi_spec \ + $api_spec \ --workers=2 \ --wait-for-schema=500 \ --max-response-time=5000 \ @@ -37,12 +37,12 @@ test-fuzzer-api: FROM earthly/dind:alpine-3.19-docker-25.0.5-r0 RUN apk update && apk add iptables-legacy curl # workaround for https://github.com/earthly/earthly/issues/3784 COPY schemathesis-docker-compose.yml . - LET OPENAPI_SPEC="http://0.0.0.0:3030/docs/cat-gateway.json" + LET api_spec="http://0.0.0.0:3030/docs/cat-gateway.json" ARG seed WITH DOCKER \ --compose schemathesis-docker-compose.yml \ - --load schemathesis:latest=(+package-schemathesis --openapi_spec=$OPENAPI_SPEC --seed=$seed) \ + --load schemathesis:latest=(+package-schemathesis --api_spec=$api_spec --seed=$seed) \ --load event-db:latest=(../../event-db+build) \ --load cat-gateway:latest=(../+package-cat-gateway-integration) \ --service event-db \ @@ -60,4 +60,64 @@ test-fuzzer-api: IF [ -f fail ] RUN --no-cache echo "Schemathesis test failed. Check the logs for more details" && \ exit 1 + END + +nightly-package-schemathesis: + FROM python:3.12-alpine3.20 + # TODO: https://github.com/input-output-hk/catalyst-voices/issues/465 + ARG api_spec + # optional argument that can be used to pass a --hypothesis-seed to replicate specific test failures + ARG seed + ARG version=3.39.5 + + RUN apk add --no-cache gcc musl-dev + RUN python -m pip install schemathesis==$version + RUN mkdir /results + COPY ./hooks/hooks.py . + VOLUME /results + ENTRYPOINT st run --checks=all $api_spec \ + --workers=2 \ + --wait-for-schema=120 \ + --max-response-time=300 \ + --hypothesis-max-examples=1000 \ + --data-generation-method=all \ + --exclude-deprecated \ + --force-schema-version=30 \ + --show-trace \ + --force-color \ + --junit-xml=/results/junit-report.xml \ + --cassette-path=/results/cassette.yaml \ + $seed + + ARG tag="latest" + SAVE IMAGE schemathesis:$tag + +# nightly-test-fuzzer-api - Fuzzy test cat-gateway using openapi specs. +nightly-test-fuzzer-api: + FROM earthly/dind:alpine-3.19-docker-25.0.5-r0 + RUN apk update && apk add iptables-legacy curl # workaround for https://github.com/earthly/earthly/issues/3784 + COPY schemathesis-docker-compose.yml . + LET api_spec="http://0.0.0.0:3030/docs/cat-gateway.json" + ARG seed + + WITH DOCKER \ + --compose schemathesis-docker-compose.yml \ + --load schemathesis:latest=(+nightly-package-schemathesis --api_spec=$api_spec --seed=$seed) \ + --load event-db:latest=(../../event-db+build) \ + --load cat-gateway:latest=(../+package-cat-gateway-integration) \ + --service event-db \ + --service cat-gateway \ + --allow-privileged + + RUN --no-cache docker run --net=host --name=st schemathesis:latest || echo fail > fail; \ + docker cp st:/results/junit-report.xml junit-report.xml && \ + docker cp st:/results/cassette.yaml cassette.yaml + END + WAIT + SAVE ARTIFACT junit-report.xml AS LOCAL schemathesis-nightly.junit-report.xml + SAVE ARTIFACT cassette.yaml AS LOCAL cassette.yaml + END + IF [ -f fail ] + RUN --no-cache echo "Nightly schemathesis test failed. Check the logs for more details" && \ + exit 1 END \ No newline at end of file diff --git a/catalyst-gateway/tests/schemathesis_tests/blueprint.cue b/catalyst-gateway/tests/schemathesis_tests/blueprint.cue index ab7bb81baf9..134e32aaa5e 100644 --- a/catalyst-gateway/tests/schemathesis_tests/blueprint.cue +++ b/catalyst-gateway/tests/schemathesis_tests/blueprint.cue @@ -4,6 +4,7 @@ project: { ci: { targets: { "test-fuzzer-api": privileged: true + "nightly-test-fuzzer-api": privileged: true } } } From 45f8b638c605e7b8e04517e33193cbc35fa27987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20Moli=C5=84ski?= <47773413+damian-molinski@users.noreply.github.com> Date: Thu, 16 Jan 2025 10:48:06 +0100 Subject: [PATCH 4/5] feat(cat-voices): new overlay and dropshadow colors (#1526) * chore: make VoicesColorScheme properties not nullable * Dialog barrier and shadow colors --------- Co-authored-by: Oleksandr Prokhorenko --- .../apps/voices/lib/common/ext/space_ext.dart | 16 +- .../lib/pages/account/account_page.dart | 2 +- .../campaign_admin_tools_dialog.dart | 2 +- .../details/widgets/campaign_management.dart | 2 +- .../lib/pages/discovery/discovery_page.dart | 2 +- .../overall_spaces/brands_navigation.dart | 4 +- ...pload_seed_phrase_confirmation_dialog.dart | 2 +- .../wallet_link/account_role_dialog.dart | 2 +- .../stage/rbac_transaction_panel.dart | 2 +- .../widgets/exit_confirm_dialog.dart | 2 +- .../registration/widgets/wallet_summary.dart | 2 +- .../widgets/buttons/voices_icon_button.dart | 4 +- .../widgets/cards/campaign_stage_card.dart | 2 +- .../widgets/cards/funded_proposal_card.dart | 2 +- .../lib/widgets/cards/role_chooser_card.dart | 2 +- .../voices/lib/widgets/chips/voices_chip.dart | 2 +- .../containers/roles_summary_container.dart | 2 +- .../workspace_text_tile_container.dart | 2 +- .../lib/widgets/dropdown/voices_dropdown.dart | 2 +- .../widgets/indicators/voices_indicator.dart | 8 +- .../widgets/modals/voices_desktop_dialog.dart | 2 +- .../modals/voices_upload_file_dialog.dart | 6 +- .../widgets/pickers/voices_time_picker.dart | 2 +- .../widgets/text_field/seed_phrase_field.dart | 2 +- .../voices_date_time_text_field.dart | 2 +- .../widgets/text_field/voices_text_field.dart | 4 +- .../lib/widgets/tiles/selectable_tile.dart | 2 +- .../lib/widgets/toggles/voices_checkbox.dart | 2 +- .../theme_extensions/voices_color_scheme.dart | 374 ++++++++++-------- .../lib/src/themes/catalyst.dart | 13 +- .../lib/src/themes/widgets/buttons_theme.dart | 8 +- .../lib/src/themes/widgets/toggles_theme.dart | 8 +- .../themes/widgets/voices_dialog_theme.dart | 16 + .../voices_input_decoration_theme.dart | 6 +- .../lib/examples/voices_avatar_example.dart | 2 +- .../lib/examples/voices_badge_example.dart | 2 +- .../lib/examples/voices_modals_example.dart | 2 +- 37 files changed, 278 insertions(+), 239 deletions(-) create mode 100644 catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/voices_dialog_theme.dart diff --git a/catalyst_voices/apps/voices/lib/common/ext/space_ext.dart b/catalyst_voices/apps/voices/lib/common/ext/space_ext.dart index b71cfe26f93..c399cc5c4ea 100644 --- a/catalyst_voices/apps/voices/lib/common/ext/space_ext.dart +++ b/catalyst_voices/apps/voices/lib/common/ext/space_ext.dart @@ -41,19 +41,19 @@ extension SpaceExt on Space { Color backgroundColor(BuildContext context) => switch (this) { Space.discovery => - Theme.of(context).colors.iconsSecondary!.withOpacity(0.16), + Theme.of(context).colors.iconsSecondary.withOpacity(0.16), Space.workspace => Theme.of(context).colorScheme.primaryContainer, - Space.voting => Theme.of(context).colors.warningContainer!, + Space.voting => Theme.of(context).colors.warningContainer, Space.fundedProjects => - Theme.of(context).colors.iconsSecondary!.withOpacity(0.16), - Space.treasury => Theme.of(context).colors.successContainer!, + Theme.of(context).colors.iconsSecondary.withOpacity(0.16), + Space.treasury => Theme.of(context).colors.successContainer, }; Color foregroundColor(BuildContext context) => switch (this) { - Space.discovery => Theme.of(context).colors.iconsSecondary!, + Space.discovery => Theme.of(context).colors.iconsSecondary, Space.workspace => Theme.of(context).colorScheme.primary, - Space.voting => Theme.of(context).colors.iconsWarning!, - Space.fundedProjects => Theme.of(context).colors.iconsSecondary!, - Space.treasury => Theme.of(context).colors.iconsSuccess!, + Space.voting => Theme.of(context).colors.iconsWarning, + Space.fundedProjects => Theme.of(context).colors.iconsSecondary, + Space.treasury => Theme.of(context).colors.iconsSuccess, }; } diff --git a/catalyst_voices/apps/voices/lib/pages/account/account_page.dart b/catalyst_voices/apps/voices/lib/pages/account/account_page.dart index c288bbfaaf8..8862b4f0794 100644 --- a/catalyst_voices/apps/voices/lib/pages/account/account_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/account/account_page.dart @@ -120,7 +120,7 @@ class _KeychainCard extends StatelessWidget { Radius.circular(16), ), border: Border.all( - color: Theme.of(context).colors.outlineBorderVariant!, + color: Theme.of(context).colors.outlineBorderVariant, width: 1, ), color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv1White, diff --git a/catalyst_voices/apps/voices/lib/pages/campaign/admin_tools/campaign_admin_tools_dialog.dart b/catalyst_voices/apps/voices/lib/pages/campaign/admin_tools/campaign_admin_tools_dialog.dart index c83dd1977db..634a1671534 100644 --- a/catalyst_voices/apps/voices/lib/pages/campaign/admin_tools/campaign_admin_tools_dialog.dart +++ b/catalyst_voices/apps/voices/lib/pages/campaign/admin_tools/campaign_admin_tools_dialog.dart @@ -163,7 +163,7 @@ class CampaignAdminToolsDialog extends StatelessWidget { color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv1White, borderRadius: BorderRadius.circular(16), border: Border.all( - color: Theme.of(context).colors.onSurfaceNeutral012!, + color: Theme.of(context).colors.onSurfaceNeutral012, width: 1, ), ), diff --git a/catalyst_voices/apps/voices/lib/pages/campaign/details/widgets/campaign_management.dart b/catalyst_voices/apps/voices/lib/pages/campaign/details/widgets/campaign_management.dart index 0e1b9463dde..3ca93482df5 100644 --- a/catalyst_voices/apps/voices/lib/pages/campaign/details/widgets/campaign_management.dart +++ b/catalyst_voices/apps/voices/lib/pages/campaign/details/widgets/campaign_management.dart @@ -82,7 +82,7 @@ class _CampaignStatusIndicator extends StatelessWidget { decoration: BoxDecoration( color: currentStatus == campaignStatus ? theme.colors.success - : theme.colors.onSurfaceNeutral012?.withOpacity(.12), + : theme.colors.onSurfaceNeutral012.withOpacity(.12), borderRadius: BorderRadius.circular(8), ), child: Padding( diff --git a/catalyst_voices/apps/voices/lib/pages/discovery/discovery_page.dart b/catalyst_voices/apps/voices/lib/pages/discovery/discovery_page.dart index b4db9d6f1ec..ece5d20da1e 100644 --- a/catalyst_voices/apps/voices/lib/pages/discovery/discovery_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/discovery/discovery_page.dart @@ -112,7 +112,7 @@ class _Segment extends StatelessWidget { child: Container( decoration: BoxDecoration( color: theme.colors.elevationsOnSurfaceNeutralLv1White, - border: Border.all(color: theme.colors.outlineBorderVariant!), + border: Border.all(color: theme.colors.outlineBorderVariant), borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.all(16), diff --git a/catalyst_voices/apps/voices/lib/pages/overall_spaces/brands_navigation.dart b/catalyst_voices/apps/voices/lib/pages/overall_spaces/brands_navigation.dart index 72082995827..40373a17d76 100644 --- a/catalyst_voices/apps/voices/lib/pages/overall_spaces/brands_navigation.dart +++ b/catalyst_voices/apps/voices/lib/pages/overall_spaces/brands_navigation.dart @@ -166,7 +166,7 @@ final class _BackgroundColor implements WidgetStateProperty { @override Color? resolve(Set states) { if (states.contains(WidgetState.selected)) { - return colors.onSurfacePrimaryContainer?.withOpacity(0.12); + return colors.onSurfacePrimaryContainer.withOpacity(0.12); } return Colors.transparent; @@ -181,7 +181,7 @@ final class _ForegroundColor implements WidgetStateProperty { @override Color? resolve(Set states) { if (states.contains(WidgetState.disabled)) { - return colors.textOnPrimaryLevel0?.withOpacity(0.3); + return colors.textOnPrimaryLevel0.withOpacity(0.3); } return colors.textOnPrimaryLevel0; diff --git a/catalyst_voices/apps/voices/lib/pages/registration/upload_seed_phrase_confirmation_dialog.dart b/catalyst_voices/apps/voices/lib/pages/registration/upload_seed_phrase_confirmation_dialog.dart index d5fec619e9e..e3cb38e26d4 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/upload_seed_phrase_confirmation_dialog.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/upload_seed_phrase_confirmation_dialog.dart @@ -34,7 +34,7 @@ class UploadSeedPhraseConfirmationDialog extends StatelessWidget { color: Theme.of(context).colors.iconsError, ), border: Border.all( - color: Theme.of(context).colors.iconsError!, + color: Theme.of(context).colors.iconsError, width: 3, ), ), diff --git a/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/account_role_dialog.dart b/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/account_role_dialog.dart index 1b670d444ec..270bba61c30 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/account_role_dialog.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/account_role_dialog.dart @@ -134,7 +134,7 @@ class _InfoContainer extends StatelessWidget { color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv1White, borderRadius: BorderRadius.circular(10), border: Border.all( - color: Theme.of(context).colors.outlineBorderVariant!, + color: Theme.of(context).colors.outlineBorderVariant, ), ), child: child, diff --git a/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/rbac_transaction_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/rbac_transaction_panel.dart index 11e4598ee23..c3c0f25358d 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/rbac_transaction_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/rbac_transaction_panel.dart @@ -166,7 +166,7 @@ class _Summary extends StatelessWidget { borderRadius: BorderRadius.circular(8), border: Border.all( width: 1.5, - color: Theme.of(context).colors.outlineBorderVariant!, + color: Theme.of(context).colors.outlineBorderVariant, ), ), child: Column( diff --git a/catalyst_voices/apps/voices/lib/pages/registration/widgets/exit_confirm_dialog.dart b/catalyst_voices/apps/voices/lib/pages/registration/widgets/exit_confirm_dialog.dart index 95d0948a673..e952ba0bd53 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/widgets/exit_confirm_dialog.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/widgets/exit_confirm_dialog.dart @@ -80,7 +80,7 @@ class _WarningIcon extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final color = theme.colors.iconsError!; + final color = theme.colors.iconsError; return VoicesAvatar( border: Border.all( diff --git a/catalyst_voices/apps/voices/lib/pages/registration/widgets/wallet_summary.dart b/catalyst_voices/apps/voices/lib/pages/registration/widgets/wallet_summary.dart index 59006f07430..a17839ef699 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/widgets/wallet_summary.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/widgets/wallet_summary.dart @@ -27,7 +27,7 @@ class WalletSummary extends StatelessWidget { borderRadius: BorderRadius.circular(8), border: Border.all( width: 1.5, - color: Theme.of(context).colors.outlineBorderVariant!, + color: Theme.of(context).colors.outlineBorderVariant, ), ), child: Column( diff --git a/catalyst_voices/apps/voices/lib/widgets/buttons/voices_icon_button.dart b/catalyst_voices/apps/voices/lib/widgets/buttons/voices_icon_button.dart index 9476318b3ff..e1ee79eb71a 100644 --- a/catalyst_voices/apps/voices/lib/widgets/buttons/voices_icon_button.dart +++ b/catalyst_voices/apps/voices/lib/widgets/buttons/voices_icon_button.dart @@ -93,10 +93,10 @@ class VoicesIconButton extends StatelessWidget { side: WidgetStateProperty.resolveWith( (states) { if (states.contains(WidgetState.disabled)) { - return BorderSide(color: colors.onSurfaceNeutral012!); + return BorderSide(color: colors.onSurfaceNeutral012); } - return BorderSide(color: colors.outlineBorderVariant!); + return BorderSide(color: colors.outlineBorderVariant); }, ), ), diff --git a/catalyst_voices/apps/voices/lib/widgets/cards/campaign_stage_card.dart b/catalyst_voices/apps/voices/lib/widgets/cards/campaign_stage_card.dart index c1aae85b85c..8e2014cb738 100644 --- a/catalyst_voices/apps/voices/lib/widgets/cards/campaign_stage_card.dart +++ b/catalyst_voices/apps/voices/lib/widgets/cards/campaign_stage_card.dart @@ -22,7 +22,7 @@ class CampaignStageCard extends StatelessWidget { decoration: BoxDecoration( color: theme.colors.elevationsOnSurfaceNeutralLv1White, border: Border.all( - color: theme.colors.outlineBorderVariant!, + color: theme.colors.outlineBorderVariant, ), borderRadius: BorderRadius.circular(20), ), diff --git a/catalyst_voices/apps/voices/lib/widgets/cards/funded_proposal_card.dart b/catalyst_voices/apps/voices/lib/widgets/cards/funded_proposal_card.dart index aed06e0da37..12d82b8a204 100644 --- a/catalyst_voices/apps/voices/lib/widgets/cards/funded_proposal_card.dart +++ b/catalyst_voices/apps/voices/lib/widgets/cards/funded_proposal_card.dart @@ -195,7 +195,7 @@ class _FundsAndComments extends StatelessWidget { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), decoration: BoxDecoration( - color: Theme.of(context).colors.success?.withOpacity(0.08), + color: Theme.of(context).colors.success.withOpacity(0.08), borderRadius: BorderRadius.circular(8), ), child: Row( diff --git a/catalyst_voices/apps/voices/lib/widgets/cards/role_chooser_card.dart b/catalyst_voices/apps/voices/lib/widgets/cards/role_chooser_card.dart index 611222ddc44..d848be69b46 100644 --- a/catalyst_voices/apps/voices/lib/widgets/cards/role_chooser_card.dart +++ b/catalyst_voices/apps/voices/lib/widgets/cards/role_chooser_card.dart @@ -54,7 +54,7 @@ class RoleChooserCard extends StatelessWidget { ? null : BoxDecoration( border: Border.all( - color: Theme.of(context).colors.outlineBorderVariant!, + color: Theme.of(context).colors.outlineBorderVariant, width: 1, ), borderRadius: BorderRadius.circular(8), diff --git a/catalyst_voices/apps/voices/lib/widgets/chips/voices_chip.dart b/catalyst_voices/apps/voices/lib/widgets/chips/voices_chip.dart index 02c09d73b00..5a7f3177d72 100644 --- a/catalyst_voices/apps/voices/lib/widgets/chips/voices_chip.dart +++ b/catalyst_voices/apps/voices/lib/widgets/chips/voices_chip.dart @@ -70,7 +70,7 @@ class VoicesChip extends StatelessWidget { border: backgroundColor != null ? null : Border.all( - color: Theme.of(context).colors.outlineBorderVariant!, + color: Theme.of(context).colors.outlineBorderVariant, ), borderRadius: borderRadius, ), diff --git a/catalyst_voices/apps/voices/lib/widgets/containers/roles_summary_container.dart b/catalyst_voices/apps/voices/lib/widgets/containers/roles_summary_container.dart index 10f243ac1c9..340ed363559 100644 --- a/catalyst_voices/apps/voices/lib/widgets/containers/roles_summary_container.dart +++ b/catalyst_voices/apps/voices/lib/widgets/containers/roles_summary_container.dart @@ -26,7 +26,7 @@ class RolesSummaryContainer extends StatelessWidget { return DecoratedBox( decoration: BoxDecoration( border: Border.all( - color: Theme.of(context).colors.outlineBorderVariant!, + color: Theme.of(context).colors.outlineBorderVariant, width: 1.5, ), borderRadius: BorderRadius.circular(8), diff --git a/catalyst_voices/apps/voices/lib/widgets/containers/workspace_text_tile_container.dart b/catalyst_voices/apps/voices/lib/widgets/containers/workspace_text_tile_container.dart index 4af7621277e..45379141fcd 100644 --- a/catalyst_voices/apps/voices/lib/widgets/containers/workspace_text_tile_container.dart +++ b/catalyst_voices/apps/voices/lib/widgets/containers/workspace_text_tile_container.dart @@ -33,7 +33,7 @@ class WorkspaceTextTileContainer extends StatelessWidget { ), boxShadow: [ BoxShadow( - color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv0!, + color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv0, offset: const Offset(0, 1), blurRadius: 4, ), diff --git a/catalyst_voices/apps/voices/lib/widgets/dropdown/voices_dropdown.dart b/catalyst_voices/apps/voices/lib/widgets/dropdown/voices_dropdown.dart index 9805a2b0700..3a0739c9233 100644 --- a/catalyst_voices/apps/voices/lib/widgets/dropdown/voices_dropdown.dart +++ b/catalyst_voices/apps/voices/lib/widgets/dropdown/voices_dropdown.dart @@ -127,7 +127,7 @@ class SingleSelectDropdown extends StatelessWidget { OutlineInputBorder _border(BuildContext context) => OutlineInputBorder( borderSide: BorderSide( - color: Theme.of(context).colors.outlineBorderVariant!, + color: Theme.of(context).colors.outlineBorderVariant, ), ); } diff --git a/catalyst_voices/apps/voices/lib/widgets/indicators/voices_indicator.dart b/catalyst_voices/apps/voices/lib/widgets/indicators/voices_indicator.dart index e1dbcacaf02..7cc4c761a2b 100644 --- a/catalyst_voices/apps/voices/lib/widgets/indicators/voices_indicator.dart +++ b/catalyst_voices/apps/voices/lib/widgets/indicators/voices_indicator.dart @@ -9,9 +9,9 @@ enum VoicesIndicatorType { Color _iconColor(BuildContext context) { return switch (this) { - VoicesIndicatorType.normal => Theme.of(context).colors.iconsForeground!, - VoicesIndicatorType.error => Theme.of(context).colors.iconsError!, - VoicesIndicatorType.success => Theme.of(context).colors.iconsSuccess!, + VoicesIndicatorType.normal => Theme.of(context).colors.iconsForeground, + VoicesIndicatorType.error => Theme.of(context).colors.iconsError, + VoicesIndicatorType.success => Theme.of(context).colors.iconsSuccess, }; } } @@ -38,7 +38,7 @@ class VoicesIndicator extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), decoration: BoxDecoration( border: Border.all( - color: theme.colors.outlineBorderVariant!, + color: theme.colors.outlineBorderVariant, width: 1, ), borderRadius: BorderRadius.circular(8), diff --git a/catalyst_voices/apps/voices/lib/widgets/modals/voices_desktop_dialog.dart b/catalyst_voices/apps/voices/lib/widgets/modals/voices_desktop_dialog.dart index e76cdeb296f..0f00cc9b304 100644 --- a/catalyst_voices/apps/voices/lib/widgets/modals/voices_desktop_dialog.dart +++ b/catalyst_voices/apps/voices/lib/widgets/modals/voices_desktop_dialog.dart @@ -122,7 +122,7 @@ class _VoicesDesktopDialog extends StatelessWidget { ? RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( - color: Theme.of(context).colors.outlineBorderVariant!, + color: Theme.of(context).colors.outlineBorderVariant, ), ) : Theme.of(context).dialogTheme.shape, diff --git a/catalyst_voices/apps/voices/lib/widgets/modals/voices_upload_file_dialog.dart b/catalyst_voices/apps/voices/lib/widgets/modals/voices_upload_file_dialog.dart index 3d6ccc2073b..4d9644995c1 100644 --- a/catalyst_voices/apps/voices/lib/widgets/modals/voices_upload_file_dialog.dart +++ b/catalyst_voices/apps/voices/lib/widgets/modals/voices_upload_file_dialog.dart @@ -174,7 +174,7 @@ class _InfoContainer extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( - color: Theme.of(context).colors.iconsPrimary!, + color: Theme.of(context).colors.iconsPrimary, ), ), child: Row( @@ -243,7 +243,7 @@ class _UploadContainerState extends State<_UploadContainer> { borderType: BorderType.RRect, radius: const Radius.circular(12), dashPattern: const [8, 6], - color: Theme.of(context).colors.iconsPrimary!, + color: Theme.of(context).colors.iconsPrimary, child: Stack( children: [ // We allow drag&drop only on web @@ -303,7 +303,7 @@ class _UploadContainerState extends State<_UploadContainer> { decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( - color: Theme.of(context).colors.iconsPrimary!, + color: Theme.of(context).colors.iconsPrimary, width: 3, ), ), diff --git a/catalyst_voices/apps/voices/lib/widgets/pickers/voices_time_picker.dart b/catalyst_voices/apps/voices/lib/widgets/pickers/voices_time_picker.dart index 36c79ccaf4e..4c85a5df8c0 100644 --- a/catalyst_voices/apps/voices/lib/widgets/pickers/voices_time_picker.dart +++ b/catalyst_voices/apps/voices/lib/widgets/pickers/voices_time_picker.dart @@ -119,7 +119,7 @@ class _TimeText extends StatelessWidget { child: ColoredBox( color: !isSelected ? Colors.transparent - : Theme.of(context).colors.onSurfaceNeutral08!, + : Theme.of(context).colors.onSurfaceNeutral08, child: Padding( key: key, padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), diff --git a/catalyst_voices/apps/voices/lib/widgets/text_field/seed_phrase_field.dart b/catalyst_voices/apps/voices/lib/widgets/text_field/seed_phrase_field.dart index 22319666c03..7c0150f79d8 100644 --- a/catalyst_voices/apps/voices/lib/widgets/text_field/seed_phrase_field.dart +++ b/catalyst_voices/apps/voices/lib/widgets/text_field/seed_phrase_field.dart @@ -92,7 +92,7 @@ class _SeedPhraseFieldState extends State { child: DecoratedBox( decoration: BoxDecoration( border: Border.all( - color: theme.colors.outlineBorder!, + color: theme.colors.outlineBorder, width: 1.5, ), borderRadius: BorderRadius.circular(12), diff --git a/catalyst_voices/apps/voices/lib/widgets/text_field/voices_date_time_text_field.dart b/catalyst_voices/apps/voices/lib/widgets/text_field/voices_date_time_text_field.dart index b1ad08e0a86..0fcca9cead6 100644 --- a/catalyst_voices/apps/voices/lib/widgets/text_field/voices_date_time_text_field.dart +++ b/catalyst_voices/apps/voices/lib/widgets/text_field/voices_date_time_text_field.dart @@ -38,7 +38,7 @@ class VoicesDateTimeTextField extends StatelessWidget { final borderSide = !dimBorder ? BorderSide( - color: theme.colors.outlineBorderVariant!, + color: theme.colors.outlineBorderVariant, width: 0.75, ) : BorderSide( diff --git a/catalyst_voices/apps/voices/lib/widgets/text_field/voices_text_field.dart b/catalyst_voices/apps/voices/lib/widgets/text_field/voices_text_field.dart index 61171b04800..cbba4f8a13e 100644 --- a/catalyst_voices/apps/voices/lib/widgets/text_field/voices_text_field.dart +++ b/catalyst_voices/apps/voices/lib/widgets/text_field/voices_text_field.dart @@ -467,9 +467,9 @@ class _VoicesTextFieldState extends State { case VoicesTextFieldStatus.none: return orDefault; case VoicesTextFieldStatus.success: - return Theme.of(context).colors.success!; + return Theme.of(context).colors.success; case VoicesTextFieldStatus.warning: - return Theme.of(context).colors.warning!; + return Theme.of(context).colors.warning; case VoicesTextFieldStatus.error: return Theme.of(context).colorScheme.error; } diff --git a/catalyst_voices/apps/voices/lib/widgets/tiles/selectable_tile.dart b/catalyst_voices/apps/voices/lib/widgets/tiles/selectable_tile.dart index e4f6d023633..e293842c7de 100644 --- a/catalyst_voices/apps/voices/lib/widgets/tiles/selectable_tile.dart +++ b/catalyst_voices/apps/voices/lib/widgets/tiles/selectable_tile.dart @@ -23,7 +23,7 @@ class SelectableTile extends StatelessWidget { borderRadius: _borderRadius(isSelected), boxShadow: [ BoxShadow( - color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv0!, + color: Theme.of(context).colors.elevationsOnSurfaceNeutralLv0, offset: const Offset(0, 1), blurRadius: 4, ), diff --git a/catalyst_voices/apps/voices/lib/widgets/toggles/voices_checkbox.dart b/catalyst_voices/apps/voices/lib/widgets/toggles/voices_checkbox.dart index 0138d3465ba..36d165c6801 100644 --- a/catalyst_voices/apps/voices/lib/widgets/toggles/voices_checkbox.dart +++ b/catalyst_voices/apps/voices/lib/widgets/toggles/voices_checkbox.dart @@ -59,7 +59,7 @@ class VoicesCheckbox extends StatelessWidget { side: isDisabled ? BorderSide( width: 2, - color: Theme.of(context).colors.onSurfaceNeutral012!, + color: Theme.of(context).colors.onSurfaceNeutral012, ) : null, ), diff --git a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/theme_extensions/voices_color_scheme.dart b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/theme_extensions/voices_color_scheme.dart index ffb7218eba6..0724bf7f5e3 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/theme_extensions/voices_color_scheme.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/theme_extensions/voices_color_scheme.dart @@ -7,64 +7,66 @@ import 'package:flutter/material.dart'; /// attributes that can be used in the Voices application to ensure consistency. @immutable class VoicesColorScheme extends ThemeExtension { - final Color? textPrimary; - final Color? textOnPrimary; - final Color? textOnPrimaryLevel0; - final Color? textOnPrimaryLevel1; - final Color? textOnPrimaryWhite; - final Color? textOnPrimaryContainer; - final Color? textDisabled; - final Color? success; - final Color? onSuccess; - final Color? successContainer; - final Color? onSuccessContainer; - final Color? warning; - final Color? onWarning; - final Color? warningContainer; - final Color? onWarningContainer; - final Color? onSurfaceNeutral08; - final Color? onSurfaceNeutral012; - final Color? onSurfaceNeutral016; - final Color? onSurfaceNeutralOpaqueLv0; - final Color? onSurfaceNeutralOpaqueLv1; - final Color? onSurfaceNeutralOpaqueLv2; - final Color? onSurfacePrimaryContainer; - final Color? onSurfacePrimary08; - final Color? onSurfacePrimary012; - final Color? onSurfacePrimary016; - final Color? onSurfaceSecondary08; - final Color? onSurfaceSecondary012; - final Color? onSurfaceSecondary016; - final Color? onSurfaceError08; - final Color? onSurfaceError012; - final Color? onSurfaceError016; - final Color? iconsForeground; - final Color? iconsBackground; - final Color? iconsBackgroundVariant; - final Color? iconsOnImage; - final Color? iconsDisabled; - final Color? iconsPrimary; - final Color? iconsSecondary; - final Color? iconsSuccess; - final Color? iconsWarning; - final Color? iconsError; - final Color? avatarsPrimary; - final Color? avatarsSecondary; - final Color? avatarsSuccess; - final Color? avatarsWarning; - final Color? avatarsError; - final Color? elevationsOnSurfaceNeutralLv0; - final Color? elevationsOnSurfaceNeutralLv1Grey; - final Color? elevationsOnSurfaceNeutralLv1White; - final Color? elevationsOnSurfaceNeutralLv2; - final Color? outlineBorder; - final Color? outlineBorderVariant; - final Color? primary98; - final Color? primaryContainer; - final Color? onPrimaryContainer; - final Color? onErrorVariant; - final Color? errorContainer; - final Color? onErrorContainer; + final Color textPrimary; + final Color textOnPrimary; + final Color textOnPrimaryLevel0; + final Color textOnPrimaryLevel1; + final Color textOnPrimaryWhite; + final Color textOnPrimaryContainer; + final Color textDisabled; + final Color success; + final Color onSuccess; + final Color successContainer; + final Color onSuccessContainer; + final Color warning; + final Color onWarning; + final Color warningContainer; + final Color onWarningContainer; + final Color onSurfaceNeutral08; + final Color onSurfaceNeutral012; + final Color onSurfaceNeutral016; + final Color onSurfaceNeutralOpaqueLv0; + final Color onSurfaceNeutralOpaqueLv1; + final Color onSurfaceNeutralOpaqueLv2; + final Color onSurfacePrimaryContainer; + final Color onSurfacePrimary08; + final Color onSurfacePrimary012; + final Color onSurfacePrimary016; + final Color onSurfaceSecondary08; + final Color onSurfaceSecondary012; + final Color onSurfaceSecondary016; + final Color onSurfaceError08; + final Color onSurfaceError012; + final Color onSurfaceError016; + final Color iconsForeground; + final Color iconsBackground; + final Color iconsBackgroundVariant; + final Color iconsOnImage; + final Color iconsDisabled; + final Color iconsPrimary; + final Color iconsSecondary; + final Color iconsSuccess; + final Color iconsWarning; + final Color iconsError; + final Color avatarsPrimary; + final Color avatarsSecondary; + final Color avatarsSuccess; + final Color avatarsWarning; + final Color avatarsError; + final Color elevationsOnSurfaceNeutralLv0; + final Color elevationsOnSurfaceNeutralLv1Grey; + final Color elevationsOnSurfaceNeutralLv1White; + final Color elevationsOnSurfaceNeutralLv2; + final Color outlineBorder; + final Color outlineBorderVariant; + final Color primary98; + final Color primaryContainer; + final Color onPrimaryContainer; + final Color onErrorVariant; + final Color errorContainer; + final Color onErrorContainer; + final Color overlay; + final Color dropShadow; const VoicesColorScheme({ required this.textPrimary, @@ -125,68 +127,72 @@ class VoicesColorScheme extends ThemeExtension { required this.onErrorVariant, required this.errorContainer, required this.onErrorContainer, + required this.overlay, + required this.dropShadow, }); @visibleForTesting const VoicesColorScheme.optional({ - this.textPrimary, - this.textOnPrimary, - this.textOnPrimaryLevel0, - this.textOnPrimaryLevel1, - this.textOnPrimaryWhite, - this.textOnPrimaryContainer, - this.textDisabled, - this.success, - this.onSuccess, - this.successContainer, - this.onSuccessContainer, - this.warning, - this.onWarning, - this.warningContainer, - this.onWarningContainer, - this.onSurfaceNeutral08, - this.onSurfaceNeutral012, - this.onSurfaceNeutral016, - this.onSurfacePrimaryContainer, - this.onSurfacePrimary08, - this.onSurfacePrimary012, - this.onSurfacePrimary016, - this.onSurfaceNeutralOpaqueLv0, - this.onSurfaceNeutralOpaqueLv1, - this.onSurfaceNeutralOpaqueLv2, - this.onSurfaceSecondary08, - this.onSurfaceSecondary012, - this.onSurfaceSecondary016, - this.onSurfaceError08, - this.onSurfaceError012, - this.onSurfaceError016, - this.iconsForeground, - this.iconsBackground, - this.iconsBackgroundVariant, - this.iconsOnImage, - this.iconsDisabled, - this.iconsPrimary, - this.iconsSecondary, - this.iconsSuccess, - this.iconsWarning, - this.iconsError, - this.avatarsPrimary, - this.avatarsSecondary, - this.avatarsSuccess, - this.avatarsWarning, - this.avatarsError, - this.elevationsOnSurfaceNeutralLv0, - this.elevationsOnSurfaceNeutralLv1Grey, - this.elevationsOnSurfaceNeutralLv1White, - this.elevationsOnSurfaceNeutralLv2, - this.outlineBorder, - this.outlineBorderVariant, - this.primary98, - this.primaryContainer, - this.onPrimaryContainer, - this.onErrorVariant, - this.errorContainer, - this.onErrorContainer, + this.textPrimary = Colors.black, + this.textOnPrimary = Colors.black, + this.textOnPrimaryLevel0 = Colors.black, + this.textOnPrimaryLevel1 = Colors.black, + this.textOnPrimaryWhite = Colors.black, + this.textOnPrimaryContainer = Colors.black, + this.textDisabled = Colors.black, + this.success = Colors.black, + this.onSuccess = Colors.black, + this.successContainer = Colors.black, + this.onSuccessContainer = Colors.black, + this.warning = Colors.black, + this.onWarning = Colors.black, + this.warningContainer = Colors.black, + this.onWarningContainer = Colors.black, + this.onSurfaceNeutral08 = Colors.black, + this.onSurfaceNeutral012 = Colors.black, + this.onSurfaceNeutral016 = Colors.black, + this.onSurfacePrimaryContainer = Colors.black, + this.onSurfacePrimary08 = Colors.black, + this.onSurfacePrimary012 = Colors.black, + this.onSurfacePrimary016 = Colors.black, + this.onSurfaceNeutralOpaqueLv0 = Colors.black, + this.onSurfaceNeutralOpaqueLv1 = Colors.black, + this.onSurfaceNeutralOpaqueLv2 = Colors.black, + this.onSurfaceSecondary08 = Colors.black, + this.onSurfaceSecondary012 = Colors.black, + this.onSurfaceSecondary016 = Colors.black, + this.onSurfaceError08 = Colors.black, + this.onSurfaceError012 = Colors.black, + this.onSurfaceError016 = Colors.black, + this.iconsForeground = Colors.black, + this.iconsBackground = Colors.black, + this.iconsBackgroundVariant = Colors.black, + this.iconsOnImage = Colors.black, + this.iconsDisabled = Colors.black, + this.iconsPrimary = Colors.black, + this.iconsSecondary = Colors.black, + this.iconsSuccess = Colors.black, + this.iconsWarning = Colors.black, + this.iconsError = Colors.black, + this.avatarsPrimary = Colors.black, + this.avatarsSecondary = Colors.black, + this.avatarsSuccess = Colors.black, + this.avatarsWarning = Colors.black, + this.avatarsError = Colors.black, + this.elevationsOnSurfaceNeutralLv0 = Colors.black, + this.elevationsOnSurfaceNeutralLv1Grey = Colors.black, + this.elevationsOnSurfaceNeutralLv1White = Colors.black, + this.elevationsOnSurfaceNeutralLv2 = Colors.black, + this.outlineBorder = Colors.black, + this.outlineBorderVariant = Colors.black, + this.primary98 = Colors.black, + this.primaryContainer = Colors.black, + this.onPrimaryContainer = Colors.black, + this.onErrorVariant = Colors.black, + this.errorContainer = Colors.black, + this.onErrorContainer = Colors.black, + this.overlay = Colors.black, + this.dropShadow = Colors.black, }); @override @@ -249,6 +255,8 @@ class VoicesColorScheme extends ThemeExtension { Color? onErrorVariant, Color? errorContainer, Color? onErrorContainer, + Color? overlay, + Color? dropShadow, }) { return VoicesColorScheme( textPrimary: textPrimary ?? this.textPrimary, @@ -321,6 +329,8 @@ class VoicesColorScheme extends ThemeExtension { onErrorVariant: onErrorVariant ?? this.onErrorVariant, errorContainer: errorContainer ?? this.errorContainer, onErrorContainer: onErrorContainer ?? this.onErrorContainer, + overlay: overlay ?? this.overlay, + dropShadow: dropShadow ?? this.dropShadow, ); } @@ -333,128 +343,142 @@ class VoicesColorScheme extends ThemeExtension { return this; } return VoicesColorScheme( - textPrimary: Color.lerp(textPrimary, other.textPrimary, t), - textOnPrimary: Color.lerp(textOnPrimary, other.textOnPrimary, t), + textPrimary: Color.lerp(textPrimary, other.textPrimary, t)!, + textOnPrimary: Color.lerp(textOnPrimary, other.textOnPrimary, t)!, textOnPrimaryLevel0: Color.lerp( textOnPrimaryLevel0, other.textOnPrimaryLevel0, t, - ), + )!, textOnPrimaryLevel1: Color.lerp( textOnPrimaryLevel1, other.textOnPrimaryLevel1, t, - ), + )!, textOnPrimaryWhite: Color.lerp( textOnPrimaryWhite, other.textOnPrimaryWhite, t, - ), + )!, textOnPrimaryContainer: Color.lerp( textOnPrimaryContainer, other.textOnPrimaryContainer, t, - ), - textDisabled: Color.lerp(textDisabled, other.textDisabled, t), - success: Color.lerp(success, other.success, t), - onSuccess: Color.lerp(onSuccess, other.onSuccess, t), - successContainer: Color.lerp(successContainer, other.successContainer, t), - onSuccessContainer: - Color.lerp(onSuccessContainer, other.onSuccessContainer, t), - warning: Color.lerp(warning, other.warning, t), - onWarning: Color.lerp(onWarning, other.onWarning, t), - warningContainer: Color.lerp(warningContainer, other.warningContainer, t), + )!, + textDisabled: Color.lerp(textDisabled, other.textDisabled, t)!, + success: Color.lerp(success, other.success, t)!, + onSuccess: Color.lerp(onSuccess, other.onSuccess, t)!, + successContainer: Color.lerp( + successContainer, + other.successContainer, + t, + )!, + onSuccessContainer: Color.lerp( + onSuccessContainer, + other.onSuccessContainer, + t, + )!, + warning: Color.lerp(warning, other.warning, t)!, + onWarning: Color.lerp(onWarning, other.onWarning, t)!, + warningContainer: + Color.lerp(warningContainer, other.warningContainer, t)!, onWarningContainer: - Color.lerp(onWarningContainer, other.onWarningContainer, t), + Color.lerp(onWarningContainer, other.onWarningContainer, t)!, onSurfaceNeutral08: - Color.lerp(onSurfaceNeutral08, other.onSurfaceNeutral08, t), + Color.lerp(onSurfaceNeutral08, other.onSurfaceNeutral08, t)!, onSurfaceNeutral012: - Color.lerp(onSurfaceNeutral012, other.onSurfaceNeutral012, t), + Color.lerp(onSurfaceNeutral012, other.onSurfaceNeutral012, t)!, onSurfaceNeutral016: - Color.lerp(onSurfaceNeutral016, other.onSurfaceNeutral016, t), + Color.lerp(onSurfaceNeutral016, other.onSurfaceNeutral016, t)!, onSurfaceNeutralOpaqueLv0: Color.lerp( onSurfaceNeutralOpaqueLv0, other.onSurfaceNeutralOpaqueLv0, t, - ), + )!, onSurfaceNeutralOpaqueLv1: Color.lerp( onSurfaceNeutralOpaqueLv1, other.onSurfaceNeutralOpaqueLv1, t, - ), + )!, onSurfaceNeutralOpaqueLv2: Color.lerp( onSurfaceNeutralOpaqueLv2, other.onSurfaceNeutralOpaqueLv2, t, - ), + )!, onSurfacePrimaryContainer: Color.lerp( onSurfacePrimaryContainer, other.onSurfacePrimaryContainer, t, - ), + )!, onSurfacePrimary08: - Color.lerp(onSurfacePrimary08, other.onSurfacePrimary08, t), + Color.lerp(onSurfacePrimary08, other.onSurfacePrimary08, t)!, onSurfacePrimary012: - Color.lerp(onSurfacePrimary012, other.onSurfacePrimary012, t), + Color.lerp(onSurfacePrimary012, other.onSurfacePrimary012, t)!, onSurfacePrimary016: - Color.lerp(onSurfacePrimary016, other.onSurfacePrimary016, t), + Color.lerp(onSurfacePrimary016, other.onSurfacePrimary016, t)!, onSurfaceSecondary08: - Color.lerp(onSurfaceSecondary08, other.onSurfaceSecondary08, t), + Color.lerp(onSurfaceSecondary08, other.onSurfaceSecondary08, t)!, onSurfaceSecondary012: - Color.lerp(onSurfaceSecondary012, other.onSurfaceSecondary012, t), + Color.lerp(onSurfaceSecondary012, other.onSurfaceSecondary012, t)!, onSurfaceSecondary016: - Color.lerp(onSurfaceSecondary016, other.onSurfaceSecondary016, t), - onSurfaceError08: Color.lerp(onSurfaceError08, other.onSurfaceError08, t), + Color.lerp(onSurfaceSecondary016, other.onSurfaceSecondary016, t)!, + onSurfaceError08: + Color.lerp(onSurfaceError08, other.onSurfaceError08, t)!, onSurfaceError012: - Color.lerp(onSurfaceError012, other.onSurfaceError012, t), + Color.lerp(onSurfaceError012, other.onSurfaceError012, t)!, onSurfaceError016: - Color.lerp(onSurfaceError016, other.onSurfaceError016, t), - iconsForeground: Color.lerp(iconsForeground, other.iconsForeground, t), - iconsBackground: Color.lerp(iconsBackground, other.iconsBackground, t), + Color.lerp(onSurfaceError016, other.onSurfaceError016, t)!, + iconsForeground: Color.lerp(iconsForeground, other.iconsForeground, t)!, + iconsBackground: Color.lerp(iconsBackground, other.iconsBackground, t)!, iconsBackgroundVariant: - Color.lerp(iconsBackgroundVariant, other.iconsBackgroundVariant, t), - iconsOnImage: Color.lerp(iconsOnImage, other.iconsOnImage, t), - iconsDisabled: Color.lerp(iconsDisabled, other.iconsDisabled, t), - iconsPrimary: Color.lerp(iconsPrimary, other.iconsPrimary, t), - iconsSecondary: Color.lerp(iconsSecondary, other.iconsSecondary, t), - iconsSuccess: Color.lerp(iconsSuccess, other.iconsSuccess, t), - iconsWarning: Color.lerp(iconsWarning, other.iconsWarning, t), - iconsError: Color.lerp(iconsError, other.iconsError, t), - avatarsPrimary: Color.lerp(avatarsPrimary, other.avatarsPrimary, t), - avatarsSecondary: Color.lerp(avatarsSecondary, other.avatarsSecondary, t), - avatarsSuccess: Color.lerp(avatarsSuccess, other.avatarsSuccess, t), - avatarsWarning: Color.lerp(avatarsWarning, other.avatarsWarning, t), - avatarsError: Color.lerp(avatarsError, other.avatarsError, t), + Color.lerp(iconsBackgroundVariant, other.iconsBackgroundVariant, t)!, + iconsOnImage: Color.lerp(iconsOnImage, other.iconsOnImage, t)!, + iconsDisabled: Color.lerp(iconsDisabled, other.iconsDisabled, t)!, + iconsPrimary: Color.lerp(iconsPrimary, other.iconsPrimary, t)!, + iconsSecondary: Color.lerp(iconsSecondary, other.iconsSecondary, t)!, + iconsSuccess: Color.lerp(iconsSuccess, other.iconsSuccess, t)!, + iconsWarning: Color.lerp(iconsWarning, other.iconsWarning, t)!, + iconsError: Color.lerp(iconsError, other.iconsError, t)!, + avatarsPrimary: Color.lerp(avatarsPrimary, other.avatarsPrimary, t)!, + avatarsSecondary: + Color.lerp(avatarsSecondary, other.avatarsSecondary, t)!, + avatarsSuccess: Color.lerp(avatarsSuccess, other.avatarsSuccess, t)!, + avatarsWarning: Color.lerp(avatarsWarning, other.avatarsWarning, t)!, + avatarsError: Color.lerp(avatarsError, other.avatarsError, t)!, elevationsOnSurfaceNeutralLv0: Color.lerp( elevationsOnSurfaceNeutralLv0, other.elevationsOnSurfaceNeutralLv0, t, - ), + )!, elevationsOnSurfaceNeutralLv1Grey: Color.lerp( elevationsOnSurfaceNeutralLv1Grey, other.elevationsOnSurfaceNeutralLv1Grey, t, - ), + )!, elevationsOnSurfaceNeutralLv1White: Color.lerp( elevationsOnSurfaceNeutralLv1White, other.elevationsOnSurfaceNeutralLv1White, t, - ), + )!, elevationsOnSurfaceNeutralLv2: Color.lerp( elevationsOnSurfaceNeutralLv2, other.elevationsOnSurfaceNeutralLv2, t, - ), - outlineBorder: Color.lerp(outlineBorder, other.outlineBorder, t), + )!, + outlineBorder: Color.lerp(outlineBorder, other.outlineBorder, t)!, outlineBorderVariant: - Color.lerp(outlineBorderVariant, other.outlineBorderVariant, t), - primary98: Color.lerp(primary98, other.primary98, t), - primaryContainer: Color.lerp(primaryContainer, other.primaryContainer, t), + Color.lerp(outlineBorderVariant, other.outlineBorderVariant, t)!, + primary98: Color.lerp(primary98, other.primary98, t)!, + primaryContainer: + Color.lerp(primaryContainer, other.primaryContainer, t)!, onPrimaryContainer: - Color.lerp(onPrimaryContainer, other.onPrimaryContainer, t), - onErrorVariant: Color.lerp(onErrorVariant, other.onErrorVariant, t), - errorContainer: Color.lerp(errorContainer, other.errorContainer, t), - onErrorContainer: Color.lerp(onErrorContainer, other.onErrorContainer, t), + Color.lerp(onPrimaryContainer, other.onPrimaryContainer, t)!, + onErrorVariant: Color.lerp(onErrorVariant, other.onErrorVariant, t)!, + errorContainer: Color.lerp(errorContainer, other.errorContainer, t)!, + onErrorContainer: + Color.lerp(onErrorContainer, other.onErrorContainer, t)!, + overlay: Color.lerp(overlay, other.overlay, t)!, + dropShadow: Color.lerp(dropShadow, other.dropShadow, t)!, ); } } diff --git a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/catalyst.dart b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/catalyst.dart index c2eebc5dbf7..3a01026e09b 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/catalyst.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/catalyst.dart @@ -4,6 +4,7 @@ import 'package:catalyst_voices_brands/src/theme_extensions/brand_assets.dart'; import 'package:catalyst_voices_brands/src/theme_extensions/voices_color_scheme.dart'; import 'package:catalyst_voices_brands/src/themes/widgets/buttons_theme.dart'; import 'package:catalyst_voices_brands/src/themes/widgets/toggles_theme.dart'; +import 'package:catalyst_voices_brands/src/themes/widgets/voices_dialog_theme.dart'; import 'package:catalyst_voices_brands/src/themes/widgets/voices_input_decoration_theme.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -85,6 +86,8 @@ const VoicesColorScheme darkVoicesColorScheme = VoicesColorScheme( onErrorVariant: VoicesColors.darkOnErrorVariant, errorContainer: VoicesColors.darkErrorContainer, onErrorContainer: VoicesColors.darkOnErrorContainer, + overlay: Color(0xA610141C), + dropShadow: Color(0xA610141C), ); const ColorScheme lightColorScheme = ColorScheme.light( @@ -167,6 +170,8 @@ const VoicesColorScheme lightVoicesColorScheme = VoicesColorScheme( onErrorVariant: VoicesColors.lightOnErrorVariant, errorContainer: VoicesColors.lightErrorContainer, onErrorContainer: VoicesColors.lightOnErrorContainer, + overlay: Color(0x9904080F), + dropShadow: Color(0x9904080F), ); /// [ThemeData] for the `catalyst` brand. @@ -315,13 +320,7 @@ ThemeData _buildThemeData( drawerTheme: DrawerThemeData( backgroundColor: voicesColorScheme.onSurfaceNeutralOpaqueLv0, ), - dialogTheme: DialogTheme( - // N10-38 - barrierColor: const Color(0x212A3D61), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - clipBehavior: Clip.hardEdge, - backgroundColor: voicesColorScheme.elevationsOnSurfaceNeutralLv1White, - ), + dialogTheme: VoicesDialogTheme(colors: voicesColorScheme), listTileTheme: ListTileThemeData( shape: const StadiumBorder(), minTileHeight: 56, diff --git a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/buttons_theme.dart b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/buttons_theme.dart index 5b6f7c2a3a3..52a03e9693a 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/buttons_theme.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/buttons_theme.dart @@ -26,14 +26,14 @@ extension ButtonsThemeExt on ThemeData { side: WidgetStateProperty.resolveWith( (states) { if (states.contains(WidgetState.disabled)) { - return BorderSide(color: colors.onSurfaceNeutral012!); + return BorderSide(color: colors.onSurfaceNeutral012); } if (states.contains(WidgetState.focused)) { return BorderSide(color: colorScheme.primary); } - return BorderSide(color: colors.outlineBorder!); + return BorderSide(color: colors.outlineBorder); }, ), ).merge(_buildBaseButtonStyle(textTheme)), @@ -72,10 +72,10 @@ extension ButtonsThemeExt on ThemeData { side: WidgetStateProperty.resolveWith( (states) { if (states.contains(WidgetState.disabled)) { - return BorderSide(color: colors.iconsDisabled!); + return BorderSide(color: colors.iconsDisabled); } - return BorderSide(color: colors.outlineBorder!); + return BorderSide(color: colors.outlineBorder); }, ), iconSize: const WidgetStatePropertyAll(18), diff --git a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/toggles_theme.dart b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/toggles_theme.dart index 949b7231d5d..d5326ae4e67 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/toggles_theme.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/toggles_theme.dart @@ -12,7 +12,7 @@ extension TogglesTheme on ThemeData { fillColor: WidgetStateProperty.resolveWith( (states) { if (states.contains(WidgetState.disabled)) { - return colors.iconsDisabled?.withOpacity(0.32); + return colors.iconsDisabled.withOpacity(0.32); } if (states.contains(WidgetState.selected)) { @@ -78,7 +78,7 @@ extension TogglesTheme on ThemeData { (states) { if (states.contains(WidgetState.disabled)) { return BorderSide( - color: colors.onSurfaceNeutral012!, + color: colors.onSurfaceNeutral012, width: 2, ); } @@ -98,7 +98,7 @@ extension TogglesTheme on ThemeData { } return BorderSide( - color: colors.outlineBorder!, + color: colors.outlineBorder, width: 2, ); }, @@ -114,7 +114,7 @@ extension TogglesTheme on ThemeData { return colorScheme.primary; } - return colors.outlineBorderVariant?.withOpacity(0.38); + return colors.outlineBorderVariant.withOpacity(0.38); }, ), trackOutlineColor: WidgetStateProperty.resolveWith( diff --git a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/voices_dialog_theme.dart b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/voices_dialog_theme.dart new file mode 100644 index 00000000000..c62033a0e55 --- /dev/null +++ b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/voices_dialog_theme.dart @@ -0,0 +1,16 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +class VoicesDialogTheme extends DialogTheme { + VoicesDialogTheme({ + required VoicesColorScheme colors, + }) : super( + barrierColor: colors.overlay, + shadowColor: colors.dropShadow, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + clipBehavior: Clip.hardEdge, + backgroundColor: colors.elevationsOnSurfaceNeutralLv1White, + ); +} diff --git a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/voices_input_decoration_theme.dart b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/voices_input_decoration_theme.dart index c7a76100da3..b5409d1adb0 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/voices_input_decoration_theme.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/voices_input_decoration_theme.dart @@ -32,7 +32,7 @@ class _Border extends MaterialStateOutlineInputBorder { if (states.contains(WidgetState.disabled)) { return OutlineInputBorder( borderSide: BorderSide( - color: colors.outlineBorder!, + color: colors.outlineBorder, width: 1, ), borderRadius: BorderRadius.circular(4), @@ -42,7 +42,7 @@ class _Border extends MaterialStateOutlineInputBorder { if (states.contains(WidgetState.error)) { return OutlineInputBorder( borderSide: BorderSide( - color: colors.iconsError!, + color: colors.iconsError, width: 2, ), borderRadius: BorderRadius.circular(4), @@ -61,7 +61,7 @@ class _Border extends MaterialStateOutlineInputBorder { return OutlineInputBorder( borderSide: BorderSide( - color: colors.outlineBorderVariant!, + color: colors.outlineBorderVariant, width: 1, ), borderRadius: BorderRadius.circular(4), diff --git a/catalyst_voices/utilities/uikit_example/lib/examples/voices_avatar_example.dart b/catalyst_voices/utilities/uikit_example/lib/examples/voices_avatar_example.dart index 5c1c42ae9a1..4f3ae128b4a 100644 --- a/catalyst_voices/utilities/uikit_example/lib/examples/voices_avatar_example.dart +++ b/catalyst_voices/utilities/uikit_example/lib/examples/voices_avatar_example.dart @@ -38,7 +38,7 @@ class VoicesAvatarExample extends StatelessWidget { icon: VoicesAssets.icons.lightBulb.buildIcon(), foregroundColor: Theme.of(context).colors.iconsSecondary, backgroundColor: - Theme.of(context).colors.iconsSecondary?.withOpacity(0.16), + Theme.of(context).colors.iconsSecondary.withOpacity(0.16), ), VoicesAvatar( icon: Image.asset(UiKitAssets.images.robotAvatar.path), diff --git a/catalyst_voices/utilities/uikit_example/lib/examples/voices_badge_example.dart b/catalyst_voices/utilities/uikit_example/lib/examples/voices_badge_example.dart index 3fd662d88d1..54297a54bb2 100644 --- a/catalyst_voices/utilities/uikit_example/lib/examples/voices_badge_example.dart +++ b/catalyst_voices/utilities/uikit_example/lib/examples/voices_badge_example.dart @@ -10,7 +10,7 @@ class VoicesBadgeExample extends StatelessWidget { Widget build(BuildContext context) { final colors = [ Theme.of(context).colorScheme.error, - Theme.of(context).colors.success!, + Theme.of(context).colors.success, ]; return Scaffold( diff --git a/catalyst_voices/utilities/uikit_example/lib/examples/voices_modals_example.dart b/catalyst_voices/utilities/uikit_example/lib/examples/voices_modals_example.dart index 9d6286621e8..c3bd20de3ea 100644 --- a/catalyst_voices/utilities/uikit_example/lib/examples/voices_modals_example.dart +++ b/catalyst_voices/utilities/uikit_example/lib/examples/voices_modals_example.dart @@ -68,7 +68,7 @@ class VoicesModalsExample extends StatelessWidget { color: Theme.of(context).colors.iconsError, ), border: Border.all( - color: Theme.of(context).colors.iconsError!, + color: Theme.of(context).colors.iconsError, width: 3, ), ), From 2f3bbbe78755ce1104d4570d6d2d052fc31775c5 Mon Sep 17 00:00:00 2001 From: Janusz Janus Date: Thu, 16 Jan 2025 13:30:32 +0100 Subject: [PATCH 5/5] test(cat-voices): Store and restore seeds during onboarding create (#1490) --- .../pageobject/common_page.dart | 7 +- .../pageobject/onboarding_page.dart | 469 +++++++++++---- .../pageobject/spaces_drawer_page.dart | 19 +- .../integration_test/suites/account_test.dart | 3 +- .../integration_test/suites/app_test.dart | 15 +- .../suites/onboarding_test.dart | 561 +++++++++++++++++- .../types/password_validation_states.dart | 5 + .../integration_test/utils/constants.dart | 9 + .../utils/selector_utils.dart | 6 +- .../integration_test/utils/test_context.dart | 42 ++ .../finish_account_creation_panel.dart | 2 + .../recover/recover_method_panel.dart | 5 + .../wallet_link/stage/intro_panel.dart | 1 + .../stage/select_wallet_panel.dart | 2 + .../pages/registration/widgets/next_step.dart | 1 + .../widgets/registration_tile.dart | 1 + .../widgets/unlock_password_form.dart | 2 + .../voices_password_strength_indicator.dart | 2 + .../seed_phrase/seed_phrases_sequencer.dart | 1 + .../separators/voices_text_divider.dart | 1 + .../widgets/text_field/voices_text_field.dart | 1 + 21 files changed, 987 insertions(+), 168 deletions(-) create mode 100644 catalyst_voices/apps/voices/integration_test/types/password_validation_states.dart create mode 100644 catalyst_voices/apps/voices/integration_test/utils/constants.dart create mode 100644 catalyst_voices/apps/voices/integration_test/utils/test_context.dart diff --git a/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart b/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart index 6732e683fb6..12296dfb200 100644 --- a/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart +++ b/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; class CommonPage { - static const decoratorData = Key('DecoratorData'); - static const decoratorIconBefore = Key('DecoratorIconBefore'); - static const decoratorIconAfter = Key('DecoratorIconAfter'); + static const decorData = Key('DecoratorData'); + static const decorIconBefore = Key('DecoratorIconBefore'); + static const decorIconAfter = Key('DecoratorIconAfter'); static const dialogCloseButton = Key('DialogCloseButton'); + static const voicesTextField = Key('VoicesTextField'); } diff --git a/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart b/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart index 5a10edf034b..ab8291b3988 100644 --- a/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart +++ b/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:patrol_finders/patrol_finders.dart'; +import '../types/password_validation_states.dart'; import '../types/registration_state.dart'; import '../utils/selector_utils.dart'; +import '../utils/test_context.dart'; import '../utils/translations_utils.dart'; import 'common_page.dart'; @@ -26,6 +28,32 @@ class OnboardingPage { static const seedPhraseStoredCheckbox = Key('SeedPhraseStoredCheckbox'); static const uploadKeyButton = Key('UploadKeyButton'); static const resetButton = Key('ResetButton'); + static const seedPhrasesPicker = Key('SeedPhrasesPicker'); + static const nextStepTitle = Key('NextStepTitle'); + static const nextStepBody = Key('NextStepBody'); + static const passwordInputField = Key('PasswordInputField'); + static const passwordConfirmInputField = Key('PasswordConfirmInputField'); + static const passwordStrengthIndicator = Key('PasswordStrengthIndicator'); + static const passwordStrengthLabel = Key('PasswordStrengthLabel'); + static const finishAccountCreationPanel = Key('FinishAccountCreationPanel'); + static const finishAccountKeychainCreated = + Key('StepRegistrationProgressStepGroup.createKeychainRowKey'); + static const finishAccountLinkWallet = + Key('StepRegistrationProgressStepGroup.linkWalletRowKey'); + static const finishAccountAccountComplete = + Key('StepRegistrationProgressStepGroup.accountCompletedRowKey'); + static const linkWalletAndRolesButton = Key('LinkWalletAndRolesButton'); + static const chooseCardanoWalletButton = Key('ChooseCardanoWalletButton'); + static const seeAllSupportedWalletsBtn = Key('SeeAllSupportedWalletsButton'); + static const walletsLinkBuilder = Key('WalletsLinkBuilder'); + static const recoverKeychainMethodsTitle = Key('RecoverKeychainMethodsTitle'); + static const keychainNotFoundIndicator = Key('KeychainNotFoundIndicator'); + static const onDeviceKeychainsWidget = Key('BlocOnDeviceKeychains'); + static const recoverKeychainMethodsSubtitle = + Key('RecoverKeychainMethodsSubtitle'); + static const recoverKeychainMethodsListTitle = + Key('RecoverKeychainMethodsListTitle'); + static const registrationTileTitle = Key('RegistrationTileTitle'); static Future writedownSeedPhraseNumber( PatrolTester $, @@ -37,37 +65,41 @@ class OnboardingPage { return int.parse(rawNumber!.split('.').first); } - static Future writedownSeedPhraseWord( + static Future writedownSeedPhraseWord( PatrolTester $, int index, ) async { - final rawNumber = + final rawWord = $(Key('SeedPhrase${index}CellKey')).$(const Key('SeedPhraseWord')).text; - return int.parse(rawNumber!.split('.').first); + return rawWord!; } - static Future inputSeedPhraseCompleterWord( + static Future inputSeedPhraseCompleterText( PatrolTester $, int index, ) async { - final seedWord = await getChildNodeText( - $, - $(Key('CompleterSeedPhrase${index}CellKey')).$(CommonPage.decoratorData), - ); + final seedWord = $(Key('CompleterSeedPhrase${index}CellKey')) + .$(CommonPage.decorData) + .$(Text) + .text; return seedWord!; } - static Future inputSeedPhrasePickerWord( + static Future inputSeedPhrasePickerText( PatrolTester $, int index, ) async { - final seedWord = await getChildNodeText( - $, - $(Key('PickerSeedPhrase${index + 1}CellKey')), - ); + final seedWord = $(Key('PickerSeedPhrase${index + 1}CellKey')).$(Text).text; return seedWord!; } + static Future inputSeedPhrasePicker( + PatrolTester $, + String word, + ) async { + return $(seedPhrasesPicker).$(find.text(word)); + } + static Future infoPartHeaderTitleText(PatrolTester $) async { return $(registrationInfoPanel).$(headerTitle).text; } @@ -86,44 +118,25 @@ class OnboardingPage { .waitUntilVisible(); } - static Future getChildNodeText( - PatrolTester $, - FinderBase parent, - ) async { - final child = find.descendant( - of: parent, - matching: find.byType(Text), - ); - return $(child).text; - } - static Finder infoPartTaskPicture(PatrolTester $) { - final child = find.descendant( - of: $(registrationInfoPanel).$(registrationInfoPictureContainer), - matching: find.byType(IconTheme), - ); - return child; + return $(registrationInfoPanel) + .$(registrationInfoPictureContainer) + .$(IconTheme); } - static void voicesFilledButtonIsEnabled( + static void voicesButtonIsEnabled( PatrolTester $, Key button, ) { - final child = find.descendant( - of: $(button), - matching: find.byType(FilledButton), - ); + final child = $(button).$(FilledButton); SelectorUtils.isEnabled($, $(child)); } - static void voicesFilledButtonIsDisabled( + static void voicesButtonIsDisabled( PatrolTester $, Key button, ) { - final child = find.descendant( - of: $(button), - matching: find.byType(FilledButton), - ); + final child = $(button).$(FilledButton); SelectorUtils.isDisabled($, $(child)); } @@ -172,10 +185,7 @@ class OnboardingPage { expect(await infoPartHeaderTitleText($), T.get('Get Started')); expect(infoPartTaskPicture($), findsOneWidget); expect( - await getChildNodeText( - $, - $(registrationInfoPanel).$(CommonPage.decoratorData), - ), + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, T.get('Learn More'), ); break; @@ -185,10 +195,7 @@ class OnboardingPage { expect(infoPartTaskPicture($), findsOneWidget); expect($(progressBar), findsOneWidget); expect( - await getChildNodeText( - $, - $(registrationInfoPanel).$(CommonPage.decoratorData), - ), + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, T.get('Learn More'), ); break; @@ -208,10 +215,7 @@ class OnboardingPage { expect(infoPartTaskPicture($), findsOneWidget); expect($(progressBar), findsOneWidget); expect( - await getChildNodeText( - $, - $(registrationInfoPanel).$(CommonPage.decoratorData), - ), + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, T.get('Learn More'), ); break; @@ -220,12 +224,10 @@ class OnboardingPage { expect(infoPartTaskPicture($), findsOneWidget); expect($(progressBar), findsOneWidget); expect( - await getChildNodeText( - $, - $(registrationInfoPanel).$(CommonPage.decoratorData), - ), + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, T.get('Learn More'), ); + break; case RegistrationState.keychainCreateMnemonicInput: expect(await infoPartHeaderTitleText($), T.get('Catalyst Keychain')); expect( @@ -241,33 +243,95 @@ class OnboardingPage { expect(infoPartTaskPicture($), findsOneWidget); expect($(progressBar), findsOneWidget); expect( - await getChildNodeText( - $, - $(registrationInfoPanel).$(CommonPage.decoratorData), - ), + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, T.get('Learn More'), ); break; case RegistrationState.keychainCreateMnemonicVerified: - throw UnimplementedError(); + expect(await infoPartHeaderTitleText($), T.get('Catalyst Keychain')); + //temporary: check for specific picture (green checked icon) + expect(infoPartTaskPicture($), findsOneWidget); + expect($(progressBar), findsOneWidget); + expect( + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, + T.get('Learn More'), + ); + break; + case RegistrationState.passwordInfo: + expect(await infoPartHeaderTitleText($), T.get('Catalyst Keychain')); + //temporary: check for specific picture (locked icon) + expect(infoPartTaskPicture($), findsOneWidget); + expect($(progressBar), findsOneWidget); + expect( + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, + T.get('Learn More'), + ); + break; + case RegistrationState.passwordInput: + expect(await infoPartHeaderTitleText($), T.get('Catalyst Keychain')); + expect( + await infoPartHeaderSubtitleText($), + T.get('Catalyst unlock password'), + ); + expect( + await infoPartHeaderBodyText($), + T.get( + 'Please provide a password for your Catalyst Keychain.', + ), + ); + //temporary: check for specific picture (locked icon) + expect(infoPartTaskPicture($), findsOneWidget); + expect($(progressBar), findsOneWidget); + expect( + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, + T.get('Learn More'), + ); + break; + case RegistrationState.keychainCreateSuccess: + expect(await infoPartHeaderTitleText($), T.get('Catalyst Keychain')); + //temporary: check for specific picture (green key locked icon) + expect(infoPartTaskPicture($), findsOneWidget); + expect($(progressBar), findsOneWidget); + expect( + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, + T.get('Learn More'), + ); + break; case RegistrationState.keychainRestoreChoice: - throw UnimplementedError(); + expect( + await infoPartHeaderTitleText($), + T.get('Restore Catalyst keychain'), + ); + expect(infoPartTaskPicture($), findsOneWidget); + expect( + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, + T.get('Learn More'), + ); + break; case RegistrationState.keychainRestoreMnemonicInfo: throw UnimplementedError(); case RegistrationState.keychainRestoreMnemonicInput: throw UnimplementedError(); case RegistrationState.keychainRestoreSuccess: throw UnimplementedError(); - case RegistrationState.passwordInfo: - throw UnimplementedError(); - case RegistrationState.passwordInput: - throw UnimplementedError(); - case RegistrationState.keychainCreateSuccess: - throw UnimplementedError(); case RegistrationState.linkWalletInfo: - throw UnimplementedError(); case RegistrationState.linkWalletSelect: - throw UnimplementedError(); + expect( + await infoPartHeaderTitleText($), + T.get('Link keys to your Catalyst Keychain'), + ); + expect( + await infoPartHeaderSubtitleText($), + T.get('Link your Cardano wallet'), + ); + //temporary: check for specific picture (blue key icon) + expect(infoPartTaskPicture($), findsOneWidget); + expect($(progressBar), findsOneWidget); + expect( + $(registrationInfoPanel).$(CommonPage.decorData).$(Text).text, + T.get('Learn More'), + ); + break; case RegistrationState.linkWalletSuccess: throw UnimplementedError(); case RegistrationState.rolesSelect: @@ -290,17 +354,11 @@ class OnboardingPage { switch (step) { case RegistrationState.getStarted: expect( - await getChildNodeText( - $, - $(registrationDetailsPanel).$(registrationDetailsTitle), - ), + $(registrationDetailsPanel).$(registrationDetailsTitle).$(Text).text, T.get('Welcome to Catalyst'), ); expect( - await getChildNodeText( - $, - $(registrationDetailsPanel).$(registrationDetailsBody), - ), + $(registrationDetailsPanel).$(registrationDetailsBody).$(Text).text, isNotEmpty, ); expect( @@ -312,17 +370,11 @@ class OnboardingPage { break; case RegistrationState.createKeychainInfo: expect( - await getChildNodeText( - $, - $(registrationDetailsPanel).$(registrationDetailsTitle), - ), + $(registrationDetailsPanel).$(registrationDetailsTitle).$(Text).text, T.get('Create your Catalyst Keychain'), ); expect( - await getChildNodeText( - $, - $(registrationDetailsPanel).$(registrationDetailsBody), - ), + $(registrationDetailsPanel).$(registrationDetailsBody).$(Text).text, isNotEmpty, ); expect(await detailsPartCreateKeychainBtn($), findsOneWidget); @@ -330,17 +382,11 @@ class OnboardingPage { break; case RegistrationState.keychainCreated: expect( - await getChildNodeText( - $, - $(registrationDetailsPanel).$(registrationDetailsTitle), - ), + $(registrationDetailsPanel).$(registrationDetailsTitle).$(Text).text, T.get('Great! Your Catalyst Keychain 
has been created.'), ); expect( - await getChildNodeText( - $, - $(registrationDetailsPanel).$(registrationDetailsBody), - ), + $(registrationDetailsPanel).$(registrationDetailsBody).$(Text).text, isNotEmpty, ); expect($(backButton), findsOneWidget); @@ -350,15 +396,12 @@ class OnboardingPage { await writedownSeedPhrasesAreDisplayed($); expect($(downloadSeedPhraseButton), findsOneWidget); expect( - await getChildNodeText( - $, - $(downloadSeedPhraseButton).$(CommonPage.decoratorData), - ), + $(downloadSeedPhraseButton).$(CommonPage.decorData).$(Text).text, T.get('Download Catalyst key'), ); expect($(seedPhraseStoredCheckbox), findsOneWidget); expect( - await getChildNodeText($, $(seedPhraseStoredCheckbox)), + $(seedPhraseStoredCheckbox).$(Text).text, T.get('I have written down/downloaded my 12 words'), ); expect($(backButton), findsOneWidget); @@ -366,17 +409,11 @@ class OnboardingPage { break; case RegistrationState.keychainCreateMnemonicInputInfo: expect( - await getChildNodeText( - $, - $(registrationDetailsPanel).$(registrationDetailsTitle), - ), + $(registrationDetailsPanel).$(registrationDetailsTitle).$(Text).text, T.get('Check your Catalyst security keys'), ); expect( - await getChildNodeText( - $, - $(registrationDetailsPanel).$(registrationDetailsBody), - ), + $(registrationDetailsPanel).$(registrationDetailsBody).$(Text).text, isNotEmpty, ); break; @@ -384,35 +421,154 @@ class OnboardingPage { await inputSeedPhrasesAreDisplayed($); expect($(uploadKeyButton), findsOneWidget); expect( - await getChildNodeText( - $, - $(uploadKeyButton).$(CommonPage.decoratorData), - ), + $(uploadKeyButton).$(CommonPage.decorData).$(Text).text, T.get('Upload Catalyst Key'), ); expect($(backButton), findsOneWidget); expect($(nextButton), findsOneWidget); break; case RegistrationState.keychainCreateMnemonicVerified: - throw UnimplementedError(); + await $(registrationDetailsPanel) + .$(registrationDetailsTitle) + .waitUntilVisible(); + expect( + $(registrationDetailsPanel).$(registrationDetailsTitle).$(Text).text, + T.get("Nice job! You've successfully verified the seed phrase for " + 'your keychain.'), + ); + expect( + $(registrationDetailsPanel).$(registrationDetailsBody).$(Text).text, + isNotEmpty, + ); + expect( + $(nextStepTitle).$(Text).text, + T.get('Your next step'), + ); + expect( + $(nextStepBody).text, + T.get('Now let’s set your Unlock password ' + 'for this device!'), + ); + expect($(backButton), findsOneWidget); + expect($(nextButton), findsOneWidget); + break; + case RegistrationState.passwordInfo: + expect( + $(registrationDetailsPanel).$(registrationDetailsTitle).$(Text).text, + T.get('Set your Catalyst unlock password 
for this device'), + ); + expect( + $(registrationDetailsPanel).$(registrationDetailsBody).$(Text).text, + isNotEmpty, + ); + expect($(backButton), findsOneWidget); + expect($(nextButton), findsOneWidget); + break; + case RegistrationState.passwordInput: + expect( + $(registrationDetailsPanel).$(passwordInputField).$(Text).text, + T.get('Enter password'), + ); + expect( + $(registrationDetailsPanel).$(passwordConfirmInputField).$(Text).text, + T.get('Confirm password'), + ); + expect($(passwordStrengthLabel), findsNothing); + expect($(passwordStrengthIndicator), findsNothing); + expect($(backButton), findsOneWidget); + expect($(nextButton), findsOneWidget); + OnboardingPage.voicesButtonIsDisabled($, OnboardingPage.nextButton); + break; + case RegistrationState.keychainCreateSuccess: + expect( + $(finishAccountCreationPanel).$(Text).text, + T.get('Congratulations your Catalyst 
Keychain is created!'), + ); + expect( + $(finishAccountKeychainCreated).$(Text).text, + T.get('Catalyst Keychain created'), + ); + expect( + $(finishAccountLinkWallet).$(Text).text, + T.get('Link Cardano Wallet & Roles'), + ); + expect( + $(finishAccountAccountComplete).$(Text).text, + T.get('Catalyst account creation completed!'), + ); + expect( + $(nextStepTitle).$(Text).text, + T.get('Your next step'), + ); + expect( + $(nextStepBody).text, + T.get('In the next step you write your Catalyst roles and 
account ' + 'to the Cardano Mainnet.'), + ); + expect( + $(linkWalletAndRolesButton).$(Text).text, + T.get('Link your Cardano Wallet & Roles'), + ); + break; case RegistrationState.keychainRestoreChoice: - throw UnimplementedError(); + expect( + $(recoverKeychainMethodsTitle).text, + T.get('Restore your Catalyst Keychain'), + ); + expect( + $(onDeviceKeychainsWidget).$(keychainNotFoundIndicator).$(Text).text, + T.get('No Catalyst Keychain found
on this device.'), + ); + expect( + $(recoverKeychainMethodsSubtitle).text, + T.get('Not to worry, in the next step you can choose the recovery ' + 'option that applies to you for this device!'), + ); + expect( + $(recoverKeychainMethodsListTitle).text, + T.get('How do you want Restore your Catalyst Keychain?'), + ); + expect( + $(registrationTileTitle).text, + T.get('12 security words'), + ); + break; case RegistrationState.keychainRestoreMnemonicInfo: throw UnimplementedError(); case RegistrationState.keychainRestoreMnemonicInput: throw UnimplementedError(); case RegistrationState.keychainRestoreSuccess: throw UnimplementedError(); - case RegistrationState.passwordInfo: - throw UnimplementedError(); - case RegistrationState.passwordInput: - throw UnimplementedError(); - case RegistrationState.keychainCreateSuccess: - throw UnimplementedError(); case RegistrationState.linkWalletInfo: - throw UnimplementedError(); + expect( + $(registrationDetailsPanel).$(registrationDetailsTitle).$(Text).text, + T.get('Link Cardano Wallet & Catalyst Roles to you Catalyst ' + 'Keychain.'), + ); + expect( + $(registrationDetailsPanel).$(registrationDetailsBody).$(Text).text, + isNotEmpty, + ); + expect( + $(chooseCardanoWalletButton).$(Text).text, + T.get('Choose Cardano Wallet'), + ); + break; case RegistrationState.linkWalletSelect: - throw UnimplementedError(); + expect( + $(registrationDetailsPanel).$(registrationDetailsTitle).$(Text).text, + T.get( + 'Select the Cardano wallet to link\nto your Catalyst Keychain.', + ), + ); + expect( + $(registrationDetailsPanel).$(registrationDetailsBody).$(Text).text, + isNotEmpty, + ); + expect($(walletsLinkBuilder), findsOneWidget); + expect($(backButton), findsOneWidget); + expect($(seeAllSupportedWalletsBtn), findsOneWidget); + break; case RegistrationState.linkWalletSuccess: throw UnimplementedError(); case RegistrationState.rolesSelect: @@ -437,8 +593,77 @@ class OnboardingPage { static Future inputSeedPhrasesAreDisplayed(PatrolTester $) async { for (var i = 0; i < 12; i++) { - expect(await inputSeedPhrasePickerWord($, i), isNotEmpty); - expect(await inputSeedPhraseCompleterWord($, i), isNotEmpty); + expect(await inputSeedPhrasePickerText($, i), isNotEmpty); + expect(await inputSeedPhraseCompleterText($, i), isNotEmpty); + } + } + + static Future storeSeedPhrases(PatrolTester $) async { + for (var i = 0; i < 12; i++) { + final v1 = await writedownSeedPhraseWord($, i); + TestContext.save(key: 'word$i', value: v1); + } + } + + static Future enterStoredSeedPhrases(PatrolTester $) async { + for (var i = 0; i < 12; i++) { + await inputSeedPhrasePicker($, TestContext.get(key: 'word$i')).tap(); + } + } + + static Future enterPassword(PatrolTester $, String password) async { + await $(passwordInputField).enterText(password); + } + + static Future enterPasswordConfirm( + PatrolTester $, + String password, + ) async { + await $(passwordConfirmInputField).enterText(password); + } + + static void checkValidationIndicator( + PatrolTester $, + PasswordValidationStatus validationStatus, + ) { + expect($(passwordStrengthLabel), findsOneWidget); + + switch (validationStatus) { + case PasswordValidationStatus.weak: + expect( + $(passwordStrengthLabel).text, + T.get('Weak password strength'), + ); + break; + case PasswordValidationStatus.normal: + expect( + $(passwordStrengthLabel).text, + T.get('Normal password strength'), + ); + break; + case PasswordValidationStatus.good: + expect( + $(passwordStrengthLabel).text, + T.get('Good password strength'), + ); + break; + } + } + + static void passwordConfirmErrorIconIsShown( + PatrolTester $, { + bool reverse = false, + }) { + if (reverse) { + expect( + $(registrationDetailsPanel).$(CommonPage.voicesTextField).$(Icon), + findsNothing, + ); + } else { + expect( + $(registrationDetailsPanel).$(CommonPage.voicesTextField).$(Icon), + findsOneWidget, + ); } } } diff --git a/catalyst_voices/apps/voices/integration_test/pageobject/spaces_drawer_page.dart b/catalyst_voices/apps/voices/integration_test/pageobject/spaces_drawer_page.dart index b355c211485..2c03468c3f7 100644 --- a/catalyst_voices/apps/voices/integration_test/pageobject/spaces_drawer_page.dart +++ b/catalyst_voices/apps/voices/integration_test/pageobject/spaces_drawer_page.dart @@ -116,11 +116,10 @@ class SpacesDrawerPage { $(userMenuContainer(Space.treasury)).$(userSectionHeader(Space.treasury)), findsOneWidget, ); - final children = find.descendant( - of: $(userMenuContainer(Space.treasury)), - matching: $(userDrawerMenuItem), + expect( + $(userMenuContainer(Space.treasury)).$(userDrawerMenuItem), + findsAtLeast(1), ); - expect($(children), findsAtLeast(1)); } static void userVotingLooksAsExpected(PatrolTester $) { @@ -132,11 +131,10 @@ class SpacesDrawerPage { $(userMenuContainer(Space.voting)).$(userSectionHeader(Space.voting)), findsOneWidget, ); - final children = find.descendant( - of: $(userMenuContainer(Space.voting)), - matching: $(userDrawerMenuItem), + expect( + $(userMenuContainer(Space.voting)).$(userDrawerMenuItem), + findsAtLeast(1), ); - expect($(children), findsAtLeast(1)); } static void userWorkspaceLooksAsExpected(PatrolTester $) { @@ -149,10 +147,5 @@ class SpacesDrawerPage { .$(userSectionHeader(Space.workspace)), findsOneWidget, ); - final children = find.descendant( - of: $(userMenuContainer(Space.workspace)), - matching: $(userDrawerMenuItem), - ); - expect($(children), findsAtLeast(1)); } } diff --git a/catalyst_voices/apps/voices/integration_test/suites/account_test.dart b/catalyst_voices/apps/voices/integration_test/suites/account_test.dart index 6bae0391148..3ebf83182d4 100644 --- a/catalyst_voices/apps/voices/integration_test/suites/account_test.dart +++ b/catalyst_voices/apps/voices/integration_test/suites/account_test.dart @@ -9,6 +9,7 @@ import 'package:patrol_finders/patrol_finders.dart'; import '../pageobject/account_dropdown_page.dart'; import '../pageobject/app_bar_page.dart'; import '../pageobject/overall_spaces_page.dart'; +import '../utils/constants.dart'; void main() async { late final GoRouter router; @@ -34,7 +35,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(OverallSpacesPage.userShortcutBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await $(AppBarPage.accountPopupBtn).tap(); await AccountDropdownPage.accountDropdownLooksAsExpected($); await AccountDropdownPage.accountDropdownContainsSpecificData($); diff --git a/catalyst_voices/apps/voices/integration_test/suites/app_test.dart b/catalyst_voices/apps/voices/integration_test/suites/app_test.dart index 11d0f9a6d30..b52e4d56bf3 100644 --- a/catalyst_voices/apps/voices/integration_test/suites/app_test.dart +++ b/catalyst_voices/apps/voices/integration_test/suites/app_test.dart @@ -10,6 +10,7 @@ import 'package:patrol_finders/patrol_finders.dart'; import '../pageobject/app_bar_page.dart'; import '../pageobject/overall_spaces_page.dart'; import '../pageobject/spaces_drawer_page.dart'; +import '../utils/constants.dart'; import '../utils/selector_utils.dart'; void main() async { @@ -34,7 +35,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(OverallSpacesPage.visitorShortcutBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); expect($(AppBarPage.spacesDrawerButton).exists, false); }, ); @@ -46,7 +47,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(OverallSpacesPage.guestShortcutBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await $(AppBarPage.spacesDrawerButton).waitUntilVisible().tap(); SpacesDrawerPage.commonElementsLookAsExpected($); @@ -66,7 +67,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(OverallSpacesPage.guestShortcutBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await $(AppBarPage.spacesDrawerButton).waitUntilVisible().tap(); // iterate thru spaces by clicking next @@ -92,7 +93,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(OverallSpacesPage.userShortcutBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await $(AppBarPage.spacesDrawerButton).waitUntilVisible().tap(); SpacesDrawerPage.commonElementsLookAsExpected($); for (final space in Space.values) { @@ -107,7 +108,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(OverallSpacesPage.guestShortcutBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await $(AppBarPage.spacesDrawerButton).waitUntilVisible().tap(); await $(SpacesDrawerPage.allSpacesBtn).tap(); expect($(OverallSpacesPage.spacesListView), findsOneWidget); @@ -119,7 +120,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(OverallSpacesPage.userShortcutBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await $(AppBarPage.spacesDrawerButton).waitUntilVisible().tap(); await $(SpacesDrawerPage.allSpacesBtn).tap(); expect($(OverallSpacesPage.spacesListView), findsOneWidget); @@ -138,7 +139,7 @@ void main() async { }; await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(OverallSpacesPage.userShortcutBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await $(AppBarPage.spacesDrawerButton).waitUntilVisible().tap(); for (final space in Space.values) { await $(SpacesDrawerPage.chooserItem(space)).tap(); diff --git a/catalyst_voices/apps/voices/integration_test/suites/onboarding_test.dart b/catalyst_voices/apps/voices/integration_test/suites/onboarding_test.dart index 3a520bc4664..62d832c33cc 100644 --- a/catalyst_voices/apps/voices/integration_test/suites/onboarding_test.dart +++ b/catalyst_voices/apps/voices/integration_test/suites/onboarding_test.dart @@ -9,7 +9,11 @@ import 'package:patrol_finders/patrol_finders.dart'; import '../pageobject/app_bar_page.dart'; import '../pageobject/onboarding_page.dart'; import '../pageobject/overall_spaces_page.dart'; +import '../types/password_validation_states.dart'; import '../types/registration_state.dart'; +import '../utils/constants.dart'; +import '../utils/test_context.dart'; +import '../utils/translations_utils.dart'; void main() async { late final GoRouter router; @@ -25,6 +29,7 @@ void main() async { tearDown(() async { await restartDependencies(); + TestContext.clearContext(); }); group( @@ -35,7 +40,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(OverallSpacesPage.visitorShortcutBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await $(AppBarPage.getStartedBtn).tap(); expect($(OnboardingPage.registrationInfoPanel), findsOneWidget); expect($(OnboardingPage.registrationDetailsPanel), findsOneWidget); @@ -47,7 +52,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.onboardingScreenLooksAsExpected( $, RegistrationState.getStarted, @@ -60,7 +65,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.closeBtn($).tap(); expect($(OnboardingPage.registrationDialog), findsNothing); }, @@ -71,7 +76,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.onboardingScreenLooksAsExpected( $, @@ -85,7 +90,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await ($(OnboardingPage.backButton)).waitUntilVisible().tap(); await OnboardingPage.registrationInfoPanelLooksAsExpected( @@ -100,12 +105,12 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); await OnboardingPage.onboardingScreenLooksAsExpected( $, - RegistrationState.createKeychainInfo, + RegistrationState.keychainCreated, ); }, ); @@ -115,7 +120,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); await ($(OnboardingPage.backButton)).waitUntilVisible().tap(); @@ -130,7 +135,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); await $(OnboardingPage.nextButton).tap(); @@ -145,7 +150,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); await $(OnboardingPage.nextButton).tap(); @@ -162,7 +167,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); await $(OnboardingPage.nextButton).tap(); @@ -170,10 +175,7 @@ void main() async { $, RegistrationState.keychainCreateMnemonicWritedown, ); - OnboardingPage.voicesFilledButtonIsDisabled( - $, - OnboardingPage.nextButton, - ); + OnboardingPage.voicesButtonIsDisabled($, OnboardingPage.nextButton); }, ); @@ -182,7 +184,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); await $(OnboardingPage.nextButton).tap(); @@ -200,7 +202,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); await $(OnboardingPage.nextButton).tap(); @@ -219,7 +221,7 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); await $(OnboardingPage.nextButton).tap(); @@ -227,6 +229,7 @@ void main() async { await $(OnboardingPage.nextButton).tap(); await $(OnboardingPage.nextButton).tap(); //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 await $(OnboardingPage.resetButton).tap(); await OnboardingPage.onboardingScreenLooksAsExpected( $, @@ -240,13 +243,86 @@ void main() async { (PatrolTester $) async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) - .tap(settleTimeout: const Duration(seconds: 10)); + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await ($(OnboardingPage.backButton)).waitUntilVisible().tap(); + await OnboardingPage.registrationInfoPanelLooksAsExpected( + $, + RegistrationState.keychainCreateMnemonicInputInfo, + ); + }, + ); + + patrolWidgetTest( + 'visitor - create - mnemonic input - correct words unlock next button', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531//temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + OnboardingPage.voicesButtonIsEnabled($, OnboardingPage.nextButton); + }, + ); + + patrolWidgetTest( + 'visitor - create - mnemonic input verified screen looks OK', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.keychainCreateMnemonicVerified, + ); + }, + ); + + patrolWidgetTest( + 'visitor - create - mnemonic input verified screen back button works', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); await $(OnboardingPage.nextButton).tap(); await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); await ($(OnboardingPage.backButton)).waitUntilVisible().tap(); await OnboardingPage.registrationInfoPanelLooksAsExpected( $, @@ -254,6 +330,453 @@ void main() async { ); }, ); + + patrolWidgetTest( + 'visitor - create - password info screen looks OK', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.passwordInfo, + ); + }, + ); + + patrolWidgetTest( + 'visitor - create - password info screen back button works', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await ($(OnboardingPage.backButton)).waitUntilVisible().tap(); + expect( + $(OnboardingPage.nextStepBody).text, + T.get('Now let’s set your Unlock password ' + 'for this device!'), + ); + }, + ); + + patrolWidgetTest( + 'visitor - create - password input screen looks OK', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.passwordInput, + ); + }, + ); + + patrolWidgetTest( + 'visitor - create - password input screen back button works', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await ($(OnboardingPage.backButton)).waitUntilVisible().tap(); + await OnboardingPage.registrationDetailsPanelLooksAsExpected( + $, + RegistrationState.passwordInfo, + ); + }, + ); + + patrolWidgetTest( + 'visitor - create - password input - valid minimum length password', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.enterPassword($, 'Test1234'); + await OnboardingPage.enterPasswordConfirm($, 'Test1234'); + OnboardingPage.passwordConfirmErrorIconIsShown( + $, + reverse: true, + ); + OnboardingPage.checkValidationIndicator( + $, + PasswordValidationStatus.normal, + ); + OnboardingPage.passwordConfirmErrorIconIsShown( + $, + reverse: true, + ); + OnboardingPage.voicesButtonIsEnabled($, OnboardingPage.nextButton); + }, + ); + + patrolWidgetTest( + 'visitor - create - password input - valid long password', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.enterPassword($, 'Test1234Test1234'); + await OnboardingPage.enterPasswordConfirm($, 'Test1234Test1234'); + OnboardingPage.checkValidationIndicator( + $, + PasswordValidationStatus.good, + ); + OnboardingPage.passwordConfirmErrorIconIsShown( + $, + reverse: true, + ); + OnboardingPage.voicesButtonIsEnabled($, OnboardingPage.nextButton); + }, + ); + + patrolWidgetTest( + 'visitor - create - password input - too short password', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.enterPassword($, 'Test123'); + OnboardingPage.checkValidationIndicator( + $, + PasswordValidationStatus.weak, + ); + OnboardingPage.passwordConfirmErrorIconIsShown( + $, + reverse: true, + ); + OnboardingPage.voicesButtonIsDisabled($, OnboardingPage.nextButton); + }, + ); + + patrolWidgetTest( + 'visitor - create - password input - valid password, no confirmation', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.enterPassword($, 'Test1234'); + OnboardingPage.checkValidationIndicator( + $, + PasswordValidationStatus.normal, + ); + OnboardingPage.passwordConfirmErrorIconIsShown( + $, + reverse: true, + ); + OnboardingPage.voicesButtonIsDisabled($, OnboardingPage.nextButton); + }, + ); + + patrolWidgetTest( + 'visitor - create - password input - not matching confirmation', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.enterPassword($, 'Test1234'); + await OnboardingPage.enterPasswordConfirm($, 'Test123'); + OnboardingPage.checkValidationIndicator( + $, + PasswordValidationStatus.normal, + ); + OnboardingPage.passwordConfirmErrorIconIsShown($); + OnboardingPage.voicesButtonIsDisabled($, OnboardingPage.nextButton); + }, + ); + + patrolWidgetTest( + 'visitor - create - keychain created success screen looks OK', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.enterPassword($, 'Test1234'); + await OnboardingPage.enterPasswordConfirm($, 'Test1234'); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.keychainCreateSuccess, + ); + }, + ); + + patrolWidgetTest( + 'visitor - create - link wallet info screen looks OK', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.enterPassword($, 'Test1234'); + await OnboardingPage.enterPasswordConfirm($, 'Test1234'); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.linkWalletAndRolesButton).tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.linkWalletInfo, + ); + }, + ); + + patrolWidgetTest( + 'visitor - create - link wallet select screen looks OK', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.enterPassword($, 'Test1234'); + await OnboardingPage.enterPasswordConfirm($, 'Test1234'); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.linkWalletAndRolesButton).tap(); + await $(OnboardingPage.chooseCardanoWalletButton).tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.linkWalletSelect, + ); + }, + ); + + patrolWidgetTest( + 'visitor - create - link wallet select screen back button works', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedCreateNewBtn($).tap(); + await OnboardingPage.detailsPartCreateKeychainBtn($).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.storeSeedPhrases($); + await $(OnboardingPage.seedPhraseStoredCheckbox).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + //temporary: remove reset when seeds are no longer prefilled + //https://github.com/input-output-hk/catalyst-voices/issues/1531 + await $(OnboardingPage.resetButton).tap(); + await OnboardingPage.enterStoredSeedPhrases($); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.nextButton).tap(); + await OnboardingPage.enterPassword($, 'Test1234'); + await OnboardingPage.enterPasswordConfirm($, 'Test1234'); + await $(OnboardingPage.nextButton).tap(); + await $(OnboardingPage.linkWalletAndRolesButton).tap(); + await $(OnboardingPage.chooseCardanoWalletButton).tap(); + await ($(OnboardingPage.backButton)).waitUntilVisible().tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.linkWalletInfo, + ); + }, + ); + + patrolWidgetTest( + 'visitor - restore - keychain choice screen looks OK', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedRecoverBtn($).tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.keychainRestoreChoice, + ); + }, + ); + + patrolWidgetTest( + 'visitor - restore - keychain choice screen back button works', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedRecoverBtn($).tap(); + await ($(OnboardingPage.backButton)).waitUntilVisible().tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.getStarted, + ); + }, + ); + }, + skip: true, + ); + + patrolWidgetTest( + 'visitor - restore - keychain choice screen looks OK', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn).tap(settleTimeout: Time.long.duration); + await OnboardingPage.detailsPartGetStartedRecoverBtn($).tap(); + await OnboardingPage.onboardingScreenLooksAsExpected( + $, + RegistrationState.keychainRestoreChoice, + ); }, ); } diff --git a/catalyst_voices/apps/voices/integration_test/types/password_validation_states.dart b/catalyst_voices/apps/voices/integration_test/types/password_validation_states.dart new file mode 100644 index 00000000000..a0ee5e6297b --- /dev/null +++ b/catalyst_voices/apps/voices/integration_test/types/password_validation_states.dart @@ -0,0 +1,5 @@ +enum PasswordValidationStatus { + weak, + normal, + good; +} diff --git a/catalyst_voices/apps/voices/integration_test/utils/constants.dart b/catalyst_voices/apps/voices/integration_test/utils/constants.dart new file mode 100644 index 00000000000..b03074474cd --- /dev/null +++ b/catalyst_voices/apps/voices/integration_test/utils/constants.dart @@ -0,0 +1,9 @@ +enum Time { + veryShort(Duration(milliseconds: 500)), + short(Duration(seconds: 2)), + long(Duration(minutes: 10)); + + final Duration duration; + + const Time(this.duration); +} diff --git a/catalyst_voices/apps/voices/integration_test/utils/selector_utils.dart b/catalyst_voices/apps/voices/integration_test/utils/selector_utils.dart index 67a0c980078..9ab6c5b57e6 100644 --- a/catalyst_voices/apps/voices/integration_test/utils/selector_utils.dart +++ b/catalyst_voices/apps/voices/integration_test/utils/selector_utils.dart @@ -7,11 +7,11 @@ class SelectorUtils { PatrolFinder widget, { bool? reverse = false, }) { - final widgetProps = $.tester.widget(widget).toString().split('(').last; + final dynamic widgetProps = $.tester.widget(widget); final expectedState = reverse! ? 'enabled' : 'disabled'; expect( - widgetProps.contains('disabled'), - !reverse, + widgetProps.enabled, // ignore: avoid_dynamic_calls + reverse, reason: 'Expected $expectedState (${widget.description})', ); } diff --git a/catalyst_voices/apps/voices/integration_test/utils/test_context.dart b/catalyst_voices/apps/voices/integration_test/utils/test_context.dart new file mode 100644 index 00000000000..b3c951aecac --- /dev/null +++ b/catalyst_voices/apps/voices/integration_test/utils/test_context.dart @@ -0,0 +1,42 @@ +class TestContext { + TestContext._privateConstructor(); + static final TestContext instance = TestContext._privateConstructor(); + Map context = {}; + + static bool has({required String key}) { + return TestContext.instance.context.containsKey(key); + } + + static void save({required String key, required String value}) { + if (has(key: key)) { + throw Exception('You tried to override "$key" property. Its not allowed'); + } + TestContext.instance.context[key] = value; + } + + static void delete({required String key}) { + if (has(key: key)) { + TestContext.instance.context.remove(key); + } + } + + static void saveWithOverride({required String key, required String value}) { + if (has(key: key)) { + delete(key: key); + } + TestContext.instance.context[key] = value; + } + + static String get({required String key}) { + if (has(key: key)) { + return TestContext.instance.context[key]!; + } + throw Exception( + 'You tried to access $key property, but it does not exist.', + ); + } + + static void clearContext() { + TestContext.instance.context.clear(); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/registration/finish_account/finish_account_creation_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/finish_account/finish_account_creation_panel.dart index 137d69cb600..9e2c3140b1c 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/finish_account/finish_account_creation_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/finish_account/finish_account_creation_panel.dart @@ -13,6 +13,7 @@ class FinishAccountCreationPanel extends StatelessWidget { @override Widget build(BuildContext context) { return Column( + key: const Key('FinishAccountCreationPanel'), crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 24), @@ -78,6 +79,7 @@ class _LinkWalletAndRolesButton extends StatelessWidget { @override Widget build(BuildContext context) { return VoicesFilledButton( + key: const Key('LinkWalletAndRolesButton'), onTap: onTap, leading: VoicesAssets.icons.wallet.buildIcon(size: 18), child: Text(context.l10n.createKeychainLinkWalletAndRoles), diff --git a/catalyst_voices/apps/voices/lib/pages/registration/recover/recover_method_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/recover/recover_method_panel.dart index eb6b9c49d0c..93697d74aba 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/recover/recover_method_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/recover/recover_method_panel.dart @@ -24,6 +24,7 @@ class RecoverMethodPanel extends StatelessWidget { children: [ const SizedBox(height: 24), Text( + key: const Key('RecoverKeychainMethodsTitle'), context.l10n.recoverKeychainMethodsTitle, style: theme.textTheme.titleMedium?.copyWith(color: colorLvl1), ), @@ -31,11 +32,13 @@ class RecoverMethodPanel extends StatelessWidget { _BlocOnDeviceKeychains(onUnlockTap: _unlockKeychain), const SizedBox(height: 12), Text( + key: const Key('RecoverKeychainMethodsSubtitle'), context.l10n.recoverKeychainMethodsSubtitle, style: theme.textTheme.bodyMedium?.copyWith(color: colorLvl1), ), const SizedBox(height: 32), Text( + key: const Key('RecoverKeychainMethodsListTitle'), context.l10n.recoverKeychainMethodsListTitle, style: theme.textTheme.titleSmall?.copyWith(color: colorLvl0), ), @@ -89,6 +92,7 @@ class _BlocOnDeviceKeychains extends StatelessWidget { @override Widget build(BuildContext context) { return BlocRecoverBuilder( + key: const Key('BlocOnDeviceKeychains'), selector: (state) => state.foundKeychain, builder: (context, state) { return _OnDeviceKeychains( @@ -151,6 +155,7 @@ class _KeychainNotFoundIndicator extends StatelessWidget { @override Widget build(BuildContext context) { return VoicesIndicator( + key: const Key('KeychainNotFoundIndicator'), type: VoicesIndicatorType.error, icon: VoicesAssets.icons.exclamation, message: Text( diff --git a/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/intro_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/intro_panel.dart index 7a1de2efca9..512fe5f4b80 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/intro_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/intro_panel.dart @@ -20,6 +20,7 @@ class IntroPanel extends StatelessWidget { ), const Spacer(), VoicesFilledButton( + key: const Key('ChooseCardanoWalletButton'), leading: VoicesAssets.icons.wallet.buildIcon(), onTap: () { RegistrationCubit.of(context).nextStep(); diff --git a/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/select_wallet_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/select_wallet_panel.dart index 0a962b81d54..d4520daab78 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/select_wallet_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/wallet_link/stage/select_wallet_panel.dart @@ -60,6 +60,7 @@ class _SelectWalletPanelState extends State { ), const SizedBox(height: 10), VoicesTextButton( + key: const Key('SeeAllSupportedWalletsButton'), trailing: VoicesAssets.icons.externalLink.buildIcon(), onTap: () async => _launchSupportedWalletsLink(), child: Text(context.l10n.seeAllSupportedWallets), @@ -101,6 +102,7 @@ class _BlocWallets extends StatelessWidget { @override Widget build(BuildContext context) { return BlocWalletLinkBuilder, Exception>?>( + key: const Key('WalletsLinkBuilder'), selector: (state) => state.wallets, builder: (context, state) { return _Wallets( diff --git a/catalyst_voices/apps/voices/lib/pages/registration/widgets/next_step.dart b/catalyst_voices/apps/voices/lib/pages/registration/widgets/next_step.dart index ce7bec1065c..7f2309cab32 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/widgets/next_step.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/widgets/next_step.dart @@ -25,6 +25,7 @@ class NextStep extends StatelessWidget { const SizedBox(height: 12), if (data != null) ...[ Text( + key: const Key('NextStepBody'), data, style: theme.textTheme.bodySmall?.copyWith(color: textColor), textAlign: TextAlign.center, diff --git a/catalyst_voices/apps/voices/lib/pages/registration/widgets/registration_tile.dart b/catalyst_voices/apps/voices/lib/pages/registration/widgets/registration_tile.dart index 0db8b0ed560..dca3b9d14a6 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/widgets/registration_tile.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/widgets/registration_tile.dart @@ -47,6 +47,7 @@ class RegistrationTile extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( + key: const Key('RegistrationTileTitle'), title, maxLines: 2, overflow: TextOverflow.ellipsis, diff --git a/catalyst_voices/apps/voices/lib/pages/registration/widgets/unlock_password_form.dart b/catalyst_voices/apps/voices/lib/pages/registration/widgets/unlock_password_form.dart index 54bbf18a171..5fb20445671 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/widgets/unlock_password_form.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/widgets/unlock_password_form.dart @@ -55,6 +55,7 @@ class _UnlockPasswordTextField extends StatelessWidget { @override Widget build(BuildContext context) { return VoicesPasswordTextField( + key: const Key('PasswordInputField'), controller: controller, textInputAction: TextInputAction.next, decoration: VoicesTextFieldDecoration( @@ -78,6 +79,7 @@ class _ConfirmUnlockPasswordTextField extends StatelessWidget { @override Widget build(BuildContext context) { return VoicesPasswordTextField( + key: const Key('PasswordConfirmInputField'), controller: controller, decoration: VoicesTextFieldDecoration( labelText: context.l10n.confirmPassword, diff --git a/catalyst_voices/apps/voices/lib/widgets/indicators/voices_password_strength_indicator.dart b/catalyst_voices/apps/voices/lib/widgets/indicators/voices_password_strength_indicator.dart index 41be4d15bd4..92b9126a447 100644 --- a/catalyst_voices/apps/voices/lib/widgets/indicators/voices_password_strength_indicator.dart +++ b/catalyst_voices/apps/voices/lib/widgets/indicators/voices_password_strength_indicator.dart @@ -37,6 +37,7 @@ class _Label extends StatelessWidget { @override Widget build(BuildContext context) { return Text( + key: const Key('PasswordStrengthLabel'), switch (passwordStrength) { PasswordStrength.weak => context.l10n.weakPasswordStrength, PasswordStrength.normal => context.l10n.normalPasswordStrength, @@ -63,6 +64,7 @@ class _Indicator extends StatelessWidget { return SizedBox( height: _foregroundTrackHeight, child: LayoutBuilder( + key: const Key('PasswordStrengthIndicator'), builder: (context, constraints) { final totalWidthOfAllGaps = (PasswordStrength.values.length - 1) * _tracksGap; diff --git a/catalyst_voices/apps/voices/lib/widgets/seed_phrase/seed_phrases_sequencer.dart b/catalyst_voices/apps/voices/lib/widgets/seed_phrase/seed_phrases_sequencer.dart index 6126b193ec5..f83f8a736a1 100644 --- a/catalyst_voices/apps/voices/lib/widgets/seed_phrase/seed_phrases_sequencer.dart +++ b/catalyst_voices/apps/voices/lib/widgets/seed_phrase/seed_phrases_sequencer.dart @@ -41,6 +41,7 @@ class SeedPhrasesSequencer extends StatelessWidget { ), const SizedBox(height: 10), SeedPhrasesPicker( + key: const Key('SeedPhrasesPicker'), words: words, selectedWords: selectedWords, onWordTap: _selectWord, diff --git a/catalyst_voices/apps/voices/lib/widgets/separators/voices_text_divider.dart b/catalyst_voices/apps/voices/lib/widgets/separators/voices_text_divider.dart index c9e90b0d1c0..6758fbd6d78 100644 --- a/catalyst_voices/apps/voices/lib/widgets/separators/voices_text_divider.dart +++ b/catalyst_voices/apps/voices/lib/widgets/separators/voices_text_divider.dart @@ -47,6 +47,7 @@ class VoicesTextDivider extends StatelessWidget { Expanded(child: Divider(indent: indent)), SizedBox(width: nameGap), DefaultTextStyle( + key: const Key('NextStepTitle'), style: (theme.textTheme.bodyLarge ?? const TextStyle()) .copyWith(color: theme.colors.textOnPrimary), child: child, diff --git a/catalyst_voices/apps/voices/lib/widgets/text_field/voices_text_field.dart b/catalyst_voices/apps/voices/lib/widgets/text_field/voices_text_field.dart index cbba4f8a13e..0f497bb6d93 100644 --- a/catalyst_voices/apps/voices/lib/widgets/text_field/voices_text_field.dart +++ b/catalyst_voices/apps/voices/lib/widgets/text_field/voices_text_field.dart @@ -216,6 +216,7 @@ class _VoicesTextFieldState extends State { minHeight: widget.maxLines == null ? 65 : 48, iconBottomSpacing: widget.maxLines == null ? 18 : 0, child: TextFormField( + key: const Key('VoicesTextField'), textAlignVertical: TextAlignVertical.top, autofocus: widget.autofocus, expands: resizable,