mirror of
https://github.com/bitwarden/clients.git
synced 2026-07-04 21:05:54 +08:00
Dirt/pm 33474/setup data testids (#19807)
This commit is contained in:
parent
9b859bbdaf
commit
0d2e295ca1
@ -1,33 +1,33 @@
|
||||
<div class="tw-flex-col">
|
||||
<span bitTypography="h6" class="tw-flex tw-text-main">{{ title }}</span>
|
||||
<div class="tw-flex tw-flex-col" [attr.data-testid]="testId()">
|
||||
<span bitTypography="h6" class="tw-flex tw-text-main">{{ title() }}</span>
|
||||
<div class="tw-flex tw-items-baseline tw-gap-2">
|
||||
@if (iconClass) {
|
||||
<i class="bwi {{ iconClass }} {{ iconColorClass }}" aria-hidden="true"></i>
|
||||
@if (iconClass()) {
|
||||
<i class="bwi {{ iconClass() }} {{ iconColorClass() }}" aria-hidden="true"></i>
|
||||
}
|
||||
<span bitTypography="h3">{{ cardMetrics }}</span>
|
||||
<span bitTypography="h3">{{ cardMetrics() }}</span>
|
||||
</div>
|
||||
<div class="tw-flex tw-items-baseline tw-mt-4 tw-gap-2">
|
||||
<span bitTypography="body2">{{ metricDescription }}</span>
|
||||
<span bitTypography="body2">{{ metricDescription() }}</span>
|
||||
</div>
|
||||
@if (buttonClick.observed && buttonText) {
|
||||
@if (buttonText()) {
|
||||
<div class="tw-flex tw-items-baseline tw-mt-4 tw-gap-2">
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
size="small"
|
||||
[buttonType]="buttonType"
|
||||
[attr.aria-label]="buttonText"
|
||||
[buttonType]="buttonType()"
|
||||
[attr.aria-label]="buttonText()"
|
||||
(click)="onButtonClick()"
|
||||
>
|
||||
{{ buttonText }}
|
||||
{{ buttonText() }}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@if (showActionLink && !buttonText) {
|
||||
@if (showActionLink() && !buttonText()) {
|
||||
<div class="tw-flex tw-items-baseline tw-mt-4 tw-gap-2">
|
||||
<p bitTypography="body1">
|
||||
<a bitLink href="#" (click)="onActionClick(); $event.preventDefault()" rel="noreferrer">
|
||||
{{ actionText }}
|
||||
{{ actionText() }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { ChangeDetectionStrategy, Component, input, output } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { ButtonModule, ButtonType, LinkModule, TypographyModule } from "@bitwarden/components";
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
selector: "dirt-activity-card",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: "./activity-card.component.html",
|
||||
imports: [TypographyModule, JslibModule, LinkModule, ButtonModule],
|
||||
imports: [TypographyModule, LinkModule, ButtonModule],
|
||||
host: {
|
||||
class:
|
||||
"tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border-secondary-100 tw-border [&:not(bit-layout_*)]:tw-rounded-lg tw-rounded-lg tw-p-6 tw-min-h-56 tw-overflow-hidden",
|
||||
@ -19,87 +16,72 @@ export class ActivityCardComponent {
|
||||
/**
|
||||
* The title of the card goes here
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() title: string = "";
|
||||
readonly title = input.required<string>();
|
||||
/**
|
||||
* The card metrics text to display next to the value
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() cardMetrics: string = "";
|
||||
readonly cardMetrics = input.required<string>();
|
||||
/**
|
||||
* The description text to display below the value and metrics
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() metricDescription: string = "";
|
||||
readonly metricDescription = input.required<string>();
|
||||
|
||||
/**
|
||||
* The text to display for the action link
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() actionText: string = "";
|
||||
readonly actionText = input<string>("");
|
||||
|
||||
/**
|
||||
* Show action link
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() showActionLink: boolean = false;
|
||||
readonly showActionLink = input<boolean>(false);
|
||||
|
||||
/**
|
||||
* Icon class to display next to metrics (e.g., "bwi-exclamation-triangle").
|
||||
* If null, no icon is displayed.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() iconClass: string | null = null;
|
||||
readonly iconClass = input<string | null>(null);
|
||||
|
||||
/**
|
||||
* CSS class for icon color (e.g., "tw-text-success", "tw-text-muted").
|
||||
* Defaults to "tw-text-muted" if not provided.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() iconColorClass: string = "tw-text-muted";
|
||||
readonly iconColorClass = input<string>("tw-text-muted");
|
||||
|
||||
/**
|
||||
* Button text. If provided, a button will be displayed instead of a navigation link.
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() buttonText: string = "";
|
||||
readonly buttonText = input<string>("");
|
||||
|
||||
/**
|
||||
* Button type (e.g., "primary", "secondary")
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() buttonType: ButtonType = "primary";
|
||||
readonly buttonType = input<ButtonType>("primary");
|
||||
|
||||
/**
|
||||
* Event emitted when button is clicked
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref
|
||||
@Output() buttonClick = new EventEmitter<void>();
|
||||
readonly buttonClick = output<void>();
|
||||
|
||||
/*
|
||||
* To facilitate automated testing, provide a testId that will be
|
||||
* added as a data attribute to the root element of the card
|
||||
* (e.g., <dirt-activity-card [testId]="'my-card'">).
|
||||
* This allows tests to easily select the card using
|
||||
* the data attribute (e.g., [data-test-id="my-card"].
|
||||
*/
|
||||
readonly testId = input.required<string>();
|
||||
|
||||
/**
|
||||
* Event emitted when action link is clicked
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref
|
||||
@Output() actionClick = new EventEmitter<void>();
|
||||
readonly actionClick = output<void>();
|
||||
|
||||
constructor(private router: Router) {}
|
||||
|
||||
onButtonClick = () => {
|
||||
readonly onButtonClick = () => {
|
||||
this.buttonClick.emit();
|
||||
};
|
||||
|
||||
onActionClick = () => {
|
||||
readonly onActionClick = () => {
|
||||
this.actionClick.emit();
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<div
|
||||
class="tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-100 [&:not(bit-layout_*)]:tw-rounded-lg tw-rounded-lg tw-p-6 tw-min-h-56 tw-overflow-hidden"
|
||||
data-testid="password-change-metric-card"
|
||||
>
|
||||
<div bitTypography="h6" class="tw-mb-2">
|
||||
{{ "passwordChangeProgress" | i18n }}
|
||||
@ -7,7 +8,7 @@
|
||||
|
||||
@switch (currentView()) {
|
||||
@case (PasswordChangeViewEnum.EMPTY) {
|
||||
<div class="tw-items-start tw-mb-2">
|
||||
<div class="tw-items-start tw-mb-2" data-testid="assign-members-to-tasks-empty">
|
||||
<span bitTypography="h3">{{ "assignMembersTasksToMonitorProgress" | i18n }}</span>
|
||||
</div>
|
||||
|
||||
@ -17,7 +18,7 @@
|
||||
}
|
||||
|
||||
@case (PasswordChangeViewEnum.NO_TASKS_ASSIGNED) {
|
||||
<div class="tw-items-start tw-mb-2">
|
||||
<div class="tw-items-start tw-mb-2" data-testid="assign-members-to-tasks-no-tasks-assigned">
|
||||
<span bitTypography="h3">{{ "assignMembersTasksToMonitorProgress" | i18n }}</span>
|
||||
</div>
|
||||
|
||||
@ -38,7 +39,7 @@
|
||||
}
|
||||
|
||||
@case (PasswordChangeViewEnum.NEW_TASKS_AVAILABLE) {
|
||||
<div class="tw-items-start tw-mb-2">
|
||||
<div class="tw-items-start tw-mb-2" data-testid="assign-members-to-tasks-new-tasks-available">
|
||||
<span bitTypography="h3">{{ "assignMembersTasksToMonitorProgress" | i18n }}</span>
|
||||
</div>
|
||||
|
||||
@ -55,7 +56,7 @@
|
||||
}
|
||||
|
||||
@case (PasswordChangeViewEnum.PROGRESS) {
|
||||
<div class="tw-items-start tw-mb-2">
|
||||
<div class="tw-items-start tw-mb-2" data-testid="assign-members-to-tasks-progress">
|
||||
<span bitTypography="h3">{{ "percentageCompleted" | i18n: completedTasksPercent() }}</span>
|
||||
</div>
|
||||
|
||||
@ -66,12 +67,13 @@
|
||||
</div>
|
||||
|
||||
<div class="tw-mt-4">
|
||||
<div class="tw-flex tw-justify-between">
|
||||
<div class="tw-flex tw-justify-between" data-testid="number-of-tasks-completed">
|
||||
<div bitTypography="body2">{{ completedTasksCount() }}</div>
|
||||
<div bitTypography="body2">{{ tasksCount() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<bit-progress
|
||||
data-testid="progress-bar"
|
||||
[showText]="false"
|
||||
size="small"
|
||||
bgColor="primary"
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
actionText="{{ 'viewAtRiskMembers' | i18n }}"
|
||||
[showActionLink]="totalCriticalAppsAtRiskMemberCount > 0"
|
||||
(actionClick)="onViewAtRiskMembers()"
|
||||
[testId]="'at-risk-members-card'"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
<dirt-password-change-metric
|
||||
@ -47,6 +48,7 @@
|
||||
actionText="{{ 'viewAtRiskMembers' | i18n }}"
|
||||
[showActionLink]="totalCriticalAppsAtRiskMemberCount > 0"
|
||||
(actionClick)="onViewAtRiskMembers()"
|
||||
[testId]="'at-risk-members-card'"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
@ -70,6 +72,7 @@
|
||||
actionText="{{ 'viewAtRiskApplications' | i18n }}"
|
||||
[showActionLink]="totalCriticalAppsAtRiskCount > 0"
|
||||
(actionClick)="onViewAtRiskApplications()"
|
||||
[testId]="'critical-applications-card'"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
@ -86,6 +89,7 @@
|
||||
[buttonText]="''"
|
||||
[buttonType]="'primary'"
|
||||
(buttonClick)="onReviewNewApplications()"
|
||||
[testId]="'allCaughtUp'"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
@ -102,6 +106,7 @@
|
||||
[buttonText]="'reviewApplications' | i18n"
|
||||
[buttonType]="'primary'"
|
||||
(buttonClick)="onReviewNewApplications()"
|
||||
[testId]="'needs-review-card'"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
@ -118,6 +123,7 @@
|
||||
[buttonText]="'reviewNow' | i18n"
|
||||
[buttonType]="'primary'"
|
||||
(buttonClick)="onReviewNewApplications()"
|
||||
[testId]="'review-new-applications-card'"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div class="tw-flex tw-flex-col tw-gap-6">
|
||||
<div class="tw-flex tw-flex-col tw-gap-6" data-testid="assign-task-dialog">
|
||||
<!-- Two-column layout: Left panel (stats) and Right panel (browser extension mockup) -->
|
||||
<div class="tw-flex tw-flex-col md:tw-flex-row tw-gap-6">
|
||||
<!-- Left Panel -->
|
||||
@ -13,7 +13,7 @@
|
||||
</bit-callout>
|
||||
|
||||
<!-- Stat Box: Members with At-Risk Passwords -->
|
||||
<div class="tw-flex tw-items-start tw-gap-3">
|
||||
<div class="tw-flex tw-items-start tw-gap-3" data-testid="members-with-at-risk-passwords">
|
||||
<bit-icon-tile
|
||||
icon="bwi-users"
|
||||
variant="primary"
|
||||
@ -32,7 +32,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Stat Box: Critical Applications At-Risk -->
|
||||
<div class="tw-flex tw-items-start tw-gap-3">
|
||||
<div class="tw-flex tw-items-start tw-gap-3" data-testid="critical-applications-at-risk">
|
||||
<bit-icon-tile
|
||||
icon="bwi-desktop"
|
||||
variant="warning"
|
||||
@ -57,7 +57,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Right Panel: Browser Extension Video -->
|
||||
<div class="tw-flex tw-flex-col tw-gap-4 tw-flex-1">
|
||||
<div class="tw-flex tw-flex-col tw-gap-4 tw-flex-1" data-testid="browser-extension-video">
|
||||
<video
|
||||
class="tw-w-full tw-rounded-lg"
|
||||
autoplay
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<bit-dialog [dialogSize]="'large'">
|
||||
<bit-dialog [dialogSize]="'large'" data-testid="new-applications-dialog">
|
||||
<span bitDialogTitle>
|
||||
{{
|
||||
currentView() === DialogView.SelectApplications
|
||||
@ -47,7 +47,7 @@
|
||||
</div>
|
||||
|
||||
@if (currentView() === DialogView.SelectApplications) {
|
||||
<ng-container bitDialogFooter>
|
||||
<ng-container bitDialogFooter data-testid="select-applications-footer">
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
@ -74,7 +74,7 @@
|
||||
}
|
||||
@if (currentView() == DialogView.AssignTasks) {
|
||||
<!-- Footer: Action Buttons -->
|
||||
<ng-container bitDialogFooter>
|
||||
<ng-container bitDialogFooter data-testid="assign-tasks-footer">
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div class="tw-space-y-3">
|
||||
<div class="tw-space-y-3" data-testid="review-applications-view">
|
||||
<bit-search
|
||||
[placeholder]="'searchApps' | i18n"
|
||||
[(ngModel)]="searchText"
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<div
|
||||
class="tw-flex tw-flex-col tw-gap-4 tw-border tw-rounded-xl tw-border-solid tw-border-secondary-100 tw-p-6"
|
||||
data-testid="trend-widget-card"
|
||||
>
|
||||
<div class="tw-flex tw-justify-between tw-items-center">
|
||||
<h2>{{ "riskOverTime" | i18n }}</h2>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<div
|
||||
class="tw-w-full tw-max-w-4xl tw-p-6 sm:tw-p-8 tw-bg-background tw-rounded-xl tw-border tw-border-solid tw-border-secondary-300 tw-flex tw-flex-col lg:tw-flex-row tw-gap-6 tw-items-center"
|
||||
data-testid="empty-state-card"
|
||||
>
|
||||
<div class="tw-flex-1 tw-flex tw-flex-col tw-gap-4 sm:tw-gap-5 tw-w-full lg:tw-w-auto">
|
||||
<div class="tw-text-main tw-text-lg sm:tw-text-xl tw-font-medium tw-leading-6 sm:tw-leading-7">
|
||||
@ -12,7 +13,7 @@
|
||||
</div>
|
||||
}
|
||||
@if (benefits().length > 0) {
|
||||
<div class="tw-flex tw-flex-col tw-gap-4 sm:tw-gap-5">
|
||||
<div class="tw-flex tw-flex-col tw-gap-4 sm:tw-gap-5" data-testid="benefits-list">
|
||||
@for (benefit of benefits(); track $index) {
|
||||
<div class="tw-flex tw-items-start tw-gap-3">
|
||||
<div
|
||||
@ -56,7 +57,7 @@
|
||||
</div>
|
||||
|
||||
@if (videoSrc() || icon()) {
|
||||
<div class="tw-hidden lg:tw-block tw-flex-shrink-0">
|
||||
<div class="tw-hidden lg:tw-block tw-flex-shrink-0" data-testid="media-container-desktop">
|
||||
<div class="tw-size-64 xl:tw-size-80 tw-relative">
|
||||
@if (videoSrc()) {
|
||||
<video
|
||||
@ -81,7 +82,10 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-flex lg:tw-hidden tw-w-full tw-justify-center">
|
||||
<div
|
||||
class="tw-flex lg:tw-hidden tw-w-full tw-justify-center"
|
||||
data-testid="media-container-mobile"
|
||||
>
|
||||
<div class="tw-size-48 sm:tw-size-64 tw-relative">
|
||||
@if (videoSrc()) {
|
||||
<video
|
||||
|
||||
@ -24,7 +24,10 @@
|
||||
{{ "downloadCSV" | i18n }}
|
||||
</button>
|
||||
<ng-container>
|
||||
<div class="tw-flex tw-justify-between tw-mt-2 tw-text-muted">
|
||||
<div
|
||||
class="tw-flex tw-justify-between tw-mt-2 tw-text-muted"
|
||||
data-testid="at-risk-header"
|
||||
>
|
||||
<div bitTypography="body2" class="tw-text-sm tw-font-bold">
|
||||
{{ "email" | i18n }}
|
||||
</div>
|
||||
@ -33,7 +36,10 @@
|
||||
</div>
|
||||
</div>
|
||||
@for (member of drawerDetails.atRiskMemberDetails; track member.email) {
|
||||
<div class="tw-flex tw-justify-between tw-mt-2">
|
||||
<div
|
||||
class="tw-flex tw-justify-between tw-mt-2"
|
||||
[attr.data-testid]="'at-risk-row-' + $index"
|
||||
>
|
||||
<div>{{ member.email }}</div>
|
||||
<div>{{ member.atRiskPasswordCount }}</div>
|
||||
</div>
|
||||
@ -63,7 +69,7 @@
|
||||
</div>
|
||||
<div class="tw-mt-1">
|
||||
@for (member of drawerDetails.appAtRiskMembers?.members; track $index) {
|
||||
<div>{{ member.email }}</div>
|
||||
<div [attr.data-testid]="'at-risk-row-' + $index">{{ member.email }}</div>
|
||||
}
|
||||
</div>
|
||||
</ng-container>
|
||||
@ -95,7 +101,10 @@
|
||||
<i class="bwi bwi-download tw-mr-1"></i>
|
||||
{{ "downloadCSV" | i18n }}
|
||||
</button>
|
||||
<div class="tw-flex tw-justify-between tw-mt-2 tw-text-muted">
|
||||
<div
|
||||
class="tw-flex tw-justify-between tw-mt-2 tw-text-muted"
|
||||
data-testid="at-risk-header"
|
||||
>
|
||||
<div bitTypography="body2" class="tw-text-sm tw-font-bold">
|
||||
{{ "application" | i18n }}
|
||||
</div>
|
||||
@ -104,7 +113,10 @@
|
||||
</div>
|
||||
</div>
|
||||
@for (app of drawerDetails.atRiskAppDetails; track app.applicationName) {
|
||||
<div class="tw-flex tw-justify-between tw-mt-2">
|
||||
<div
|
||||
class="tw-flex tw-justify-between tw-mt-2"
|
||||
[attr.data-testid]="'at-risk-row-' + $index"
|
||||
>
|
||||
<div>{{ app.applicationName }}</div>
|
||||
<div>{{ app.atRiskPasswordCount }}</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user