Merge remote-tracking branch 'origin/release/13.4' into dev

This commit is contained in:
ulferts
2024-03-12 09:55:56 +01:00
34 changed files with 199 additions and 224 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ See COPYRIGHT and LICENSE files for more details.
 
</label>
<input type="submit" name="login" id="login-pulldown"
value="<%=t(:button_login)%>" class="button -primary" tabindex="1" />
value="<%=t(:button_login)%>" class="button -primary button_no-margin" tabindex="1" />
</div>
</div>
+16 -13
View File
@@ -57,21 +57,24 @@ See COPYRIGHT and LICENSE files for more details.
<%= submit_tag t(:button_login),
name: :login,
class: 'button -primary',
class: 'button -primary button_no-margin',
data: { disable_with: t(:label_loading) } %>
<div class="login-options-container">
<div class="login-links">
<% if Setting.lost_password? %>
<%= link_to t(:label_password_lost), { controller: '/account', action: 'lost_password' } %>
<br>
<% end %>
<% if Setting::SelfRegistration.enabled? %>
<%= link_to t(:label_register),
'',
title: t(:label_register),
class: 'registration-modal--activation-link' %>
<% end %>
<div class="login-options-container">
<div class="login-links">
<% if Setting.lost_password? %>
<%= link_to t(:label_password_lost),
{ controller: '/account', action: 'lost_password' },
class: 'login-form--footer-link' %>
<br>
<% end %>
<% if Setting::SelfRegistration.enabled? %>
<%= link_to t(:label_register),
'',
title: t(:label_register),
class: 'login-form--footer-link registration-modal--activation-link' %>
<% end %>
</div>
</div>
</div>
<% end %>
+10 -7
View File
@@ -384,17 +384,20 @@ en:
learn_about: "Learn more about the new features"
# Include the version to invalidate outdated translations in other locales.
# Otherwise, e.g. chinese might still have the translations for 10.0 in the 12.0 release.
"13_3":
"13_4":
standard:
learn_about_link: https://www.openproject.org/blog/openproject-13-3-release/
learn_about_link: https://www.openproject.org/blog/openproject-13-4-release/
new_features_html: >
The release contains various new features and improvements: <br>
<ul class="%{list_styling_class}">
<li>Filter and save custom project lists.</li>
<li>Separate Gantt charts module with new default views.</li>
<li>Automatically managed project folders for OneDrive/SharePoint integration.</li>
<li>Show number of "Shared users" in the share button and display them in the work packages list.</li>
<li>Improved calculations and updates for progress reporting for work package hierarchies.</li>
<li>GitLab integration (originally developed by Community contributors)</li>
<li>Advanced features for custom project lists</li>
<li>Advanced features for the Meetings module</li>
<li>Admins are nudged to go through OAuth flow when activating a storage</li>
<li>Virus scanning functionality with ClamAV (Enterprise add-on)</li>
<li>PDF Export: Lists in table cells are supported</li>
<li>WebAuthn/FIDO/U2F is added as a second factor</li>
<li>More languages added to the default available set</li>
</ul>
ical_sharing_modal:
+1 -1
View File
@@ -45,7 +45,7 @@ services:
- "caddy_data:/data"
db:
image: postgres:10
image: postgres:13
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: p4ssw0rd
@@ -26,11 +26,12 @@
[searchFn]="customSearchFn"
[virtualScroll]="false"
(focus)="onFocus()"
(blur)="onFocusOut()"
(search)="search($event)"
(close)="onClose()"
(change)="followItem($event)"
(keydown.enter)="onEnterBeforeResultsLoaded()"
(keydown.escape)="blur()"
(keydown.escape)="onFocusOut()"
(clear)="clearSearch()"
[filters]="autocompleterOptions.filters"
[resource]="autocompleterOptions.resource"
@@ -251,6 +251,8 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy {
this.selectedItem = undefined;
this.toggleTopMenuClass();
}
(<HTMLInputElement>document.activeElement).blur();
}
public onClose():void {
@@ -467,11 +469,6 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy {
}
}
public blur():void {
this.ngSelectComponent.ngSelectInstance.searchTerm = '';
(<HTMLInputElement>document.activeElement).blur();
}
private get currentScope():string {
const serviceScope = this.globalSearchService.projectScope;
return (serviceScope === '') ? 'current_project_and_all_descendants' : serviceScope;
@@ -35,10 +35,10 @@ import { imagePath } from 'core-app/shared/helpers/images/path-helper';
export const homescreenNewFeaturesBlockSelector = 'homescreen-new-features-block';
// The key used in the I18n files to distinguish between versions.
const OpVersionI18n = '13_3';
const OpVersionI18n = '13_4';
/** Update the teaser image to the next version */
const featureTeaserImage = '13_3_features.svg';
const featureTeaserImage = '13_4_features.svg';
@Component({
template: `
@@ -85,9 +85,9 @@ export class AddAssigneeComponent {
.apiV3Service
.principals
.filtered(filters)
.get()
.getPaginatedResults()
.pipe(
map((collection) => collection.elements.filter(
map((elements) => elements.filter(
(user) => !this.alreadySelected.find((selected) => selected === user.id),
)),
);
@@ -82,6 +82,7 @@ import {
import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service';
import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service';
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
import { MAGIC_PAGE_NUMBER } from 'core-app/core/apiv3/helpers/get-paginated-results';
import { CalendarDragDropService } from 'core-app/features/team-planner/team-planner/calendar-drag-drop.service';
import { StatusResource } from 'core-app/features/hal/resources/status-resource';
import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset';
@@ -223,7 +224,7 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
return this
.capabilitiesResourceService
.fetchCapabilities({ pageSize: -1, filters });
.fetchCapabilities({ pageSize: MAGIC_PAGE_NUMBER, filters });
}),
map((result) => result
._embedded
@@ -250,6 +251,7 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
filter((ids) => ids.length > 0),
map((ids) => ({
filters: [['id', '=', ids]],
pageSize: MAGIC_PAGE_NUMBER,
}) as ApiV3ListParameters),
);
@@ -8,4 +8,13 @@
</widget-menu>
</widget-header>
<op-wp-calendar [static]="true"></op-wp-calendar>
<ng-container *ngIf="{hasCapability: hasCapability$ | async} as context">
<div class="op-toast -error" *ngIf="context.hasCapability === false">
<span [textContent]="text.missing_permission"></span>
</div>
<op-wp-calendar
*ngIf="context.hasCapability === true"
[static]="true"
></op-wp-calendar>
</ng-container>
@@ -33,6 +33,7 @@ import { CurrentProjectService } from 'core-app/core/current-project/current-pro
import {
WorkPackageIsolatedQuerySpaceDirective,
} from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive';
import { CurrentUserService } from 'core-app/core/current-user/current-user.service';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -40,10 +41,19 @@ import {
hostDirectives: [WorkPackageIsolatedQuerySpaceDirective],
})
export class WidgetWpCalendarComponent extends AbstractWidgetComponent {
constructor(protected readonly i18n:I18nService,
text = {
missing_permission: this.I18n.t('js.grid.widgets.missing_permission'),
};
hasCapability$ = this.currentUser.hasCapabilities$('work_packages/read', this.currentProject.id);
constructor(
protected readonly I18n:I18nService,
protected readonly injector:Injector,
protected readonly currentProject:CurrentProjectService) {
super(i18n, injector);
protected readonly currentProject:CurrentProjectService,
protected readonly currentUser:CurrentUserService,
) {
super(I18n, injector);
}
public get projectIdentifier() {
@@ -33,8 +33,10 @@ import {
ElementRef,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewChild,
} from '@angular/core';
@@ -60,7 +62,7 @@ import SpotDropAlignmentOption from 'core-app/spot/drop-alignment-options';
templateUrl: './file-link-list-item.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FileLinkListItemComponent implements OnInit, AfterViewInit {
export class FileLinkListItemComponent implements OnInit, OnChanges, AfterViewInit {
@Input() public fileLink:IFileLink;
@Input() public allowEditing = false;
@@ -132,8 +134,16 @@ export class FileLinkListItemComponent implements OnInit, AfterViewInit {
'js.storages.file_links.download',
{ fileName: this.fileLink.originData.name },
);
}
this.floatingActions = this.getFloatingActions();
// Before, the getFloatingActions() method was called in the ngOnInit() method.
// The value of the allowEditing property can be calculated after the component is already initialized (in fact it is determined
// asynchronously, by getting a value from the server in a separate request). Therefore, the available actions need
// to be calculated whenever the value is set.
ngOnChanges(changes:SimpleChanges):void {
if (changes.allowEditing) {
this.floatingActions = this.getFloatingActions();
}
}
ngAfterViewInit():void {
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

@@ -28,16 +28,14 @@
#login-form
margin: 50px auto 0
padding-top: 20px
padding: 20px
width: 511px
word-break: break-word
.login-options-container
.login-form--footer
display: flex
justify-content: space-between
margin-bottom: 10px
.login-links
float: right
text-align: right
margin-top: -3rem
#content .login-auth-providers.wide
width: auto
@@ -63,13 +61,9 @@
margin-top: 25px
h3
border: none
margin-top: 20px
font-weight: normal
font-size: 1rem
text-decoration: none
text-align: center
position: relative
z-index: 1
@@ -101,24 +95,11 @@
@include prevent-float-collapse
margin-top: 1em
a.auth-provider
float: left
margin-left: 10px
background-color: light-grey
font-weight: normal
color: var(--body-font-color)
&:hover
text-decoration: none
.auth-provider-name
line-height: normal
white-space: normal
a.auth-provider:hover
text-decoration: none
#nav-login-content .login-auth-providers
h3
&:before
width: 100%
span
background: var(--header-drop-down-bg-color)
.login-auth-provider-list
@@ -32,31 +32,23 @@
#nav-login-content .login-auth-providers
width: 100%
#content .login-auth-providers
margin-top: 114px
h3:before
top: -14px
#login-form
.form--field-container
@include grid-content(12)
padding: 0
margin: 0
.login-options-container
position: relative
.login-form--footer
flex-direction: column
align-items: center
.login-links
float: none
position: absolute
text-align: center
width: 100%
margin-top: 0
a
display: block
.login-form--footer-link
display: inline-block
margin-top: 15px
.button
margin-right: 0
margin-left: 0
width: 100%
#new_user
@@ -54,42 +54,14 @@
.mobile-otp-new-device--body
flex: 1
#login-form input[type=radio]
float: left
#resend_otp_container
margin-top: 30px
#resend_otp_container input[type=radio]
margin-right: 8px
#resend_otp_container label
margin-right: 10px
#resend_otp_container .resend-header
font-size: 14px
font-weight: var(--base-text-weight-bold)
margin-bottom: 10px
#resend_otp_container .radios-wrapper
padding-top: 7px
float: left
border: none
overflow: hidden
clear: none
#resend_otp_container .radios-wrapper label
float: left
clear: none
display: block
padding: 2px 1em 0 0
#resend_otp_container .radios-wrapper input[type=radio]
float: left
clear: none
margin: 2px 0 15px 2px
// Add some paddings to action links
.mobile-otp--two-factor-device-row td.buttons
@@ -27,8 +27,8 @@
//++
.op-table-baseline--old-field
// Hide status bubbles in old values
&[class^=__hl_inline_status]:before
// Hide highlighting bubbles in old values
&[class^=__hl_inline_]:before
display: none
// Hide avatars in old values
@@ -4,3 +4,5 @@
margin-top: 1rem
width: 511px
@media screen and (max-width: $breakpoint-sm)
width: 100%
+1
View File
@@ -8,6 +8,7 @@ en:
text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition."
link: 'Enterprise edition.'
widgets:
missing_permission: "You don't have the necessary permissions to view this widget."
custom_text:
title: 'Custom text'
documents:
@@ -129,7 +129,7 @@ RSpec.describe 'Managing file links in work package', :js, :webmock do
end
it 'must enable the user to remove a file link' do
within_test_selector('op-tab-content--tab-section', text: 'MY STORAGE', wait: 25) do
within_test_selector('op-tab-content--tab-section', text: 'MY STORAGE') do
within(:list_item, text: 'jingle.ogg') do
page.find('span', text: 'jingle.ogg').hover
page.click_on('Remove file link')
@@ -43,9 +43,7 @@ RSpec.describe 'Work package table context menu',
team_planner.visit!
loading_indicator_saveguard
retry_block do
team_planner.add_assignee user
end
team_planner.add_assignee user
team_planner.within_lane(user) do
team_planner.expect_event work_package
@@ -77,11 +77,7 @@ RSpec.describe 'Team planner create new work package', :js, with_ee: %i[team_pla
team_planner.expect_assignee(user, present: false)
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add user.name
end
team_planner.add_assignee user.name
end
it_behaves_like 'can create a new work package'
@@ -119,23 +115,9 @@ RSpec.describe 'Team planner create new work package', :js, with_ee: %i[team_pla
team_planner.expect_assignee(other_user, present: false)
team_planner.expect_assignee(third_user, present: false)
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add other_user.name
end
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add third_user.name
end
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add user.name
end
team_planner.add_assignee other_user.name
team_planner.add_assignee third_user.name
team_planner.add_assignee user.name
end
it_behaves_like 'can create a new work package'
@@ -41,11 +41,7 @@ RSpec.describe 'Team planner working days', :js,
team_planner.visit!
team_planner.expect_empty_state
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add user.name
end
team_planner.add_assignee user.name
# Initially, in the "Work week" view, non working days are hidden
expect(page).to have_css('.fc-day-mon')
@@ -56,15 +56,11 @@ RSpec.describe 'Team planner project include', :js, with_ee: %i[team_planner_vie
work_package_view.expect_assignee(other_user, present: false)
retry_block do
work_package_view.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
work_package_view.select_user_to_add user.name
work_package_view.add_assignee user.name
end
retry_block do
work_package_view.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
work_package_view.select_user_to_add other_user.name
work_package_view.add_assignee other_user.name
end
work_package_view.expect_assignee user
@@ -155,19 +155,11 @@ RSpec.describe 'Team planner',
team_planner.expect_assignee(user, present: false)
team_planner.expect_assignee(other_user, present: false)
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add user.name
end
team_planner.add_assignee user.name
team_planner.expect_empty_state(present: false)
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add other_user.name
end
team_planner.add_assignee other_user.name
team_planner.expect_assignee user
team_planner.expect_assignee other_user
@@ -249,21 +241,13 @@ RSpec.describe 'Team planner',
team_planner.expect_assignee(user, present: false)
team_planner.expect_assignee(other_user, present: false)
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add user.name
end
team_planner.add_assignee user.name
team_planner.expect_empty_state(present: false)
team_planner.expect_assignee(user)
team_planner.expect_assignee(other_user, present: false)
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add other_user.name
end
team_planner.add_assignee other_user.name
team_planner.expect_assignee(user)
team_planner.expect_assignee(other_user)
@@ -280,11 +264,7 @@ RSpec.describe 'Team planner',
team_planner.expect_empty_state
# Try one more time to make sure deleting the full filter didn't kill the functionality
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add user.name
end
team_planner.add_assignee user.name
team_planner.expect_assignee(user)
team_planner.expect_assignee(other_user, present: false)
@@ -293,11 +273,7 @@ RSpec.describe 'Team planner',
it 'filters possible assignees correctly' do
team_planner.visit!
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.search_user_to_add user_outside_project.name
end
team_planner.search_assignee(user_outside_project.name)
expect(page).to have_css('.ng-option-disabled', text: "No items found")
@@ -307,14 +283,45 @@ RSpec.describe 'Team planner',
team_planner.expect_assignee(user)
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.search_user_to_add user.name
end
team_planner.search_assignee user.name
expect(page).to have_css('.ng-option-disabled', text: "No items found")
end
context 'when the page size is smaller than the number of assignees' do
before do
allow(Setting)
.to receive(:per_page_options_array)
.and_return([1])
end
it 'renders assignees and assignee dropdown correctly' do
team_planner.visit!
team_planner.wait_for_loaded
# Render all the available users in the select dropdown regardless of the page size
team_planner.click_add_user
team_planner.expect_user_selectable user
team_planner.expect_user_selectable other_user
team_planner.add_assignee user
team_planner.add_assignee other_user
team_planner.save_as('TP1')
page.refresh
team_planner.wait_for_loaded
# Render all the available users in the team planner regardless of the page size
team_planner.expect_assignee user
team_planner.expect_assignee other_user
# Do not render any available users in the select
team_planner.click_add_user
team_planner.expect_user_selectable user, present: false
team_planner.expect_user_selectable other_user, present: false
end
end
end
context 'with a readonly work package' do
@@ -337,11 +344,7 @@ RSpec.describe 'Team planner',
team_planner.expect_empty_state
team_planner.expect_assignee(user, present: false)
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add user.name
end
team_planner.add_assignee user.name
team_planner.expect_empty_state(present: false)
team_planner.expect_assignee user
@@ -58,9 +58,7 @@ RSpec.describe 'Team planner constraints for a subproject',
team_planner.visit!
team_planner.add_assignee user
retry_block do
team_planner.add_assignee other_user
end
team_planner.add_assignee other_user
# Include the subproject
project_include.toggle!
@@ -79,9 +79,7 @@ RSpec.describe 'Team planner drag&dop and resizing',
team_planner.visit!
team_planner.add_assignee user
retry_block do
team_planner.add_assignee other_user
end
team_planner.add_assignee other_user
team_planner.within_lane(user) do
team_planner.expect_event first_wp, present: false
@@ -247,9 +245,7 @@ RSpec.describe 'Team planner drag&dop and resizing',
team_planner.visit!
team_planner.add_assignee user
retry_block do
team_planner.add_assignee other_user
end
team_planner.add_assignee other_user
end
it 'allows neither dragging nor resizing any wp' do
@@ -36,11 +36,7 @@ RSpec.describe 'Team planner', :js, with_ee: %i[team_planner_view] do
team_planner.visit!
team_planner.expect_empty_state
retry_block do
team_planner.click_add_user
page.find("#{test_selector('tp-add-assignee')} input")
team_planner.select_user_to_add user.name
end
team_planner.add_assignee user.name
team_planner.expect_view_mode 'Work week'
expect(page).to have_css('.fc-timeline-slot-frame', count: 5)
@@ -225,9 +225,23 @@ module Pages
end
def add_assignee(name)
click_add_user
page.find("#{page.test_selector('tp-add-assignee')} input")
select_user_to_add name
retry_block do
return if page.has_selector?('.fc-resource', text: name, wait: 0)
click_add_user
page.find("#{page.test_selector('tp-add-assignee')} input")
select_user_to_add(name)
end
end
def search_assignee(name)
retry_block do
click_add_user
page.find("#{page.test_selector('tp-add-assignee')} input")
search_autocomplete page.find('[data-test-selector="tp-add-assignee"]'),
query: name,
results_selector: 'body'
end
end
def click_add_user
@@ -243,10 +257,13 @@ module Pages
results_selector: 'body'
end
def search_user_to_add(name)
search_autocomplete page.find('[data-test-selector="tp-add-assignee"]'),
query: name,
results_selector: 'body'
def expect_user_selectable(user, present: true)
name = user.is_a?(User) ? user.name : user.to_s
expect_ng_option page.find('[data-test-selector="tp-add-assignee"]'),
name,
results_selector: 'body',
present:
end
def change_wp_date_by_resizing(work_package, number_of_days:, is_start_date:)
@@ -11,7 +11,8 @@
<%= styled_text_field_tag 'backup_code', nil, required: true, autocomplete: 'off', size: 20, maxlength: 20, tabindex: 1, autofocus: true %>
</div>
</div>
<input type="submit" name="login" value="<%=t(:button_submit)%>" class="button -primary" tabindex="2" />
<input type="submit" name="login" value="<%=t(:button_submit)%>" class="button -primary button_no-margin" tabindex="2" />
<% end %>
</div>
</div>
@@ -43,7 +43,7 @@
</label>
</div>
<% end %>
<input type="submit" name="login" value="<%= t(:button_login) %>" class="button -primary" tabindex="2"/>
<input type="submit" name="login" value="<%= t(:button_login) %>" class="button -primary button_no-margin" tabindex="2"/>
<% if resend_supported || has_other_devices || has_backup_codes %>
<div class="login-options-container">
<div class="login-links">
@@ -68,15 +68,23 @@
<%= hidden_field_tag 'use_device', @service.device.id %>
<hr/>
<div class="resend-header"><%= t(:text_send_otp_again) %></div>
<div class="radios-wrapper">
<% default_channel = @service.device.channel %>
<% supported_channels = @used_device.class.supported_channels & @strategy.class.supported_channels %>
<% supported_channels.each do |channel| %>
<% default_channel = @service.device.channel %>
<% supported_channels = @used_device.class.supported_channels & @strategy.class.supported_channels %>
<% supported_channels.each do |channel| %>
<div class="form--field">
<%= radio_button_tag 'use_channel', channel, default_channel == channel %>
<label for="channel_<%= channel %>"><%= t("button_otp_by_#{channel}") %></label>
<<<<<<< HEAD
<% end %>
</div>
<input type="submit" value="<%= t(:button_resend_otp_form) %>" id="submit_resend_form" class="button -primary"/>
=======
</div>
<% end %>
<input type="submit" value="<%= t(:button_resend_otp_form) %>" id="submit_resend_form" class="button -highlight"/>
>>>>>>> origin/release/13.4
<% end %>
<% end %>
<% if has_other_devices %>
@@ -95,12 +103,13 @@
</ul>
<% end %>
<% if has_backup_codes %>
<hr/>
<div class="resend-header"><%= t('two_factor_authentication.login.enter_backup_code_title') %></div>
<ul>
<li>
<%= link_to(t('two_factor_authentication.login.enter_backup_code_title'), { action: :enter_backup_code }) %>
</a>
</li>
</ul>
<% end %>
</div>
</li>
</ul>
<% end %>
</div>
</div>
@@ -14,6 +14,6 @@
</div>
</div>
<div>
<input type="submit" name="login" value="<%= t 'button_continue' %>" class="button -primary" tabindex="2" />
<input type="submit" name="login" value="<%= t 'button_continue' %>" class="button -primary button_no-margin" tabindex="2" />
</div>
<% end %>
@@ -45,9 +45,9 @@ module Components::Autocompleter
end
end
def expect_ng_option(element, option, results_selector: nil)
def expect_ng_option(element, option, results_selector: nil, present: true)
within(ng_find_dropdown(element, results_selector:)) do
expect(page).to have_css('.ng-option', text: option)
expect(page).to have_conditional_selector(present, '.ng-option', text: option)
end
end