Fix Capybara/ClickLinkOrButtonStyle cop in spec/features (#28576)

This commit is contained in:
Matt Jankowski 2024-01-04 04:20:32 -05:00 committed by GitHub
parent 06374d07c3
commit f06c1f1552
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 49 additions and 49 deletions

View file

@ -676,7 +676,7 @@ GEM
unicode-display_width (>= 2.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0) rubocop-ast (1.30.0)
parser (>= 3.2.1.0) parser (>= 3.2.1.0)
rubocop-capybara (2.19.0) rubocop-capybara (2.20.0)
rubocop (~> 1.41) rubocop (~> 1.41)
rubocop-factory_bot (2.24.0) rubocop-factory_bot (2.24.0)
rubocop (~> 1.33) rubocop (~> 1.33)

View file

@ -22,7 +22,7 @@ describe 'Admin::Accounts' do
context 'without selecting any accounts' do context 'without selecting any accounts' do
it 'displays a notice about account selection' do it 'displays a notice about account selection' do
click_button button_for_suspend click_on button_for_suspend
expect(page).to have_content(selection_error_text) expect(page).to have_content(selection_error_text)
end end
@ -32,7 +32,7 @@ describe 'Admin::Accounts' do
it 'suspends the account' do it 'suspends the account' do
batch_checkbox_for(approved_user_account).check batch_checkbox_for(approved_user_account).check
click_button button_for_suspend click_on button_for_suspend
expect(approved_user_account.reload).to be_suspended expect(approved_user_account.reload).to be_suspended
end end
@ -42,7 +42,7 @@ describe 'Admin::Accounts' do
it 'approves the account user' do it 'approves the account user' do
batch_checkbox_for(unapproved_user_account).check batch_checkbox_for(unapproved_user_account).check
click_button button_for_approve click_on button_for_approve
expect(unapproved_user_account.reload.user).to be_approved expect(unapproved_user_account.reload.user).to be_approved
end end
@ -52,7 +52,7 @@ describe 'Admin::Accounts' do
it 'rejects and removes the account' do it 'rejects and removes the account' do
batch_checkbox_for(unapproved_user_account).check batch_checkbox_for(unapproved_user_account).check
click_button button_for_reject click_on button_for_reject
expect { unapproved_user_account.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { unapproved_user_account.reload }.to raise_error(ActiveRecord::RecordNotFound)
end end

View file

@ -16,7 +16,7 @@ describe 'Admin::CustomEmojis' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
click_button button_for_enable click_on button_for_enable
expect(page).to have_content(selection_error_text) expect(page).to have_content(selection_error_text)
end end

View file

@ -14,7 +14,7 @@ describe 'blocking domains through the moderation interface' do
fill_in 'domain_block_domain', with: 'example.com' fill_in 'domain_block_domain', with: 'example.com'
select I18n.t('admin.domain_blocks.new.severity.silence'), from: 'domain_block_severity' select I18n.t('admin.domain_blocks.new.severity.silence'), from: 'domain_block_severity'
click_button I18n.t('admin.domain_blocks.new.create') click_on I18n.t('admin.domain_blocks.new.create')
expect(DomainBlock.exists?(domain: 'example.com', severity: 'silence')).to be true expect(DomainBlock.exists?(domain: 'example.com', severity: 'silence')).to be true
expect(DomainBlockWorker).to have_received(:perform_async) expect(DomainBlockWorker).to have_received(:perform_async)
@ -27,14 +27,14 @@ describe 'blocking domains through the moderation interface' do
fill_in 'domain_block_domain', with: 'example.com' fill_in 'domain_block_domain', with: 'example.com'
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity'
click_button I18n.t('admin.domain_blocks.new.create') click_on I18n.t('admin.domain_blocks.new.create')
# It doesn't immediately block but presents a confirmation screen # It doesn't immediately block but presents a confirmation screen
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com'))
expect(DomainBlockWorker).to_not have_received(:perform_async) expect(DomainBlockWorker).to_not have_received(:perform_async)
# Confirming creates a block # Confirming creates a block
click_button I18n.t('admin.domain_blocks.confirm_suspension.confirm') click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm')
expect(DomainBlock.exists?(domain: 'example.com', severity: 'suspend')).to be true expect(DomainBlock.exists?(domain: 'example.com', severity: 'suspend')).to be true
expect(DomainBlockWorker).to have_received(:perform_async) expect(DomainBlockWorker).to have_received(:perform_async)
@ -49,14 +49,14 @@ describe 'blocking domains through the moderation interface' do
fill_in 'domain_block_domain', with: 'example.com' fill_in 'domain_block_domain', with: 'example.com'
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity'
click_button I18n.t('admin.domain_blocks.new.create') click_on I18n.t('admin.domain_blocks.new.create')
# It doesn't immediately block but presents a confirmation screen # It doesn't immediately block but presents a confirmation screen
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com'))
expect(DomainBlockWorker).to_not have_received(:perform_async) expect(DomainBlockWorker).to_not have_received(:perform_async)
# Confirming updates the block # Confirming updates the block
click_button I18n.t('admin.domain_blocks.confirm_suspension.confirm') click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm')
expect(domain_block.reload.severity).to eq 'suspend' expect(domain_block.reload.severity).to eq 'suspend'
expect(DomainBlockWorker).to have_received(:perform_async) expect(DomainBlockWorker).to have_received(:perform_async)
@ -71,14 +71,14 @@ describe 'blocking domains through the moderation interface' do
fill_in 'domain_block_domain', with: 'subdomain.example.com' fill_in 'domain_block_domain', with: 'subdomain.example.com'
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity'
click_button I18n.t('admin.domain_blocks.new.create') click_on I18n.t('admin.domain_blocks.new.create')
# It doesn't immediately block but presents a confirmation screen # It doesn't immediately block but presents a confirmation screen
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'subdomain.example.com')) expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'subdomain.example.com'))
expect(DomainBlockWorker).to_not have_received(:perform_async) expect(DomainBlockWorker).to_not have_received(:perform_async)
# Confirming creates the block # Confirming creates the block
click_button I18n.t('admin.domain_blocks.confirm_suspension.confirm') click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm')
expect(DomainBlock.where(domain: 'subdomain.example.com', severity: 'suspend')).to exist expect(DomainBlock.where(domain: 'subdomain.example.com', severity: 'suspend')).to exist
expect(DomainBlockWorker).to have_received(:perform_async) expect(DomainBlockWorker).to have_received(:perform_async)
@ -96,14 +96,14 @@ describe 'blocking domains through the moderation interface' do
visit edit_admin_domain_block_path(domain_block) visit edit_admin_domain_block_path(domain_block)
select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity'
click_button I18n.t('generic.save_changes') click_on I18n.t('generic.save_changes')
# It doesn't immediately block but presents a confirmation screen # It doesn't immediately block but presents a confirmation screen
expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com'))
expect(DomainBlockWorker).to_not have_received(:perform_async) expect(DomainBlockWorker).to_not have_received(:perform_async)
# Confirming updates the block # Confirming updates the block
click_button I18n.t('admin.domain_blocks.confirm_suspension.confirm') click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm')
expect(DomainBlockWorker).to have_received(:perform_async) expect(DomainBlockWorker).to have_received(:perform_async)
expect(domain_block.reload.severity).to eq 'suspend' expect(domain_block.reload.severity).to eq 'suspend'

View file

@ -16,7 +16,7 @@ describe 'Admin::EmailDomainBlocks' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
click_button button_for_delete click_on button_for_delete
expect(page).to have_content(selection_error_text) expect(page).to have_content(selection_error_text)
end end

View file

@ -16,7 +16,7 @@ describe 'Admin::IpBlocks' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
click_button button_for_delete click_on button_for_delete
expect(page).to have_content(selection_error_text) expect(page).to have_content(selection_error_text)
end end

View file

@ -11,13 +11,13 @@ describe 'finding software updates through the admin interface' do
it 'shows a link to the software updates page, which links to release notes' do it 'shows a link to the software updates page, which links to release notes' do
visit settings_profile_path visit settings_profile_path
click_link I18n.t('admin.critical_update_pending') click_on I18n.t('admin.critical_update_pending')
expect(page).to have_title(I18n.t('admin.software_updates.title')) expect(page).to have_title(I18n.t('admin.software_updates.title'))
expect(page).to have_content('99.99.99') expect(page).to have_content('99.99.99')
click_link I18n.t('admin.software_updates.release_notes') click_on I18n.t('admin.software_updates.release_notes')
expect(page).to have_current_path('https://github.com/mastodon/mastodon/releases/v99', url: true) expect(page).to have_current_path('https://github.com/mastodon/mastodon/releases/v99', url: true)
end end
end end

View file

@ -17,7 +17,7 @@ describe 'Admin::Statuses' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
click_button button_for_report click_on button_for_report
expect(page).to have_content(selection_error_text) expect(page).to have_content(selection_error_text)
end end

View file

@ -16,7 +16,7 @@ describe 'Admin::Trends::Links::PreviewCardProviders' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
click_button button_for_allow click_on button_for_allow
expect(page).to have_content(selection_error_text) expect(page).to have_content(selection_error_text)
end end

View file

@ -16,7 +16,7 @@ describe 'Admin::Trends::Links' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
click_button button_for_allow click_on button_for_allow
expect(page).to have_content(selection_error_text) expect(page).to have_content(selection_error_text)
end end

View file

@ -16,7 +16,7 @@ describe 'Admin::Trends::Statuses' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
click_button button_for_allow click_on button_for_allow
expect(page).to have_content(selection_error_text) expect(page).to have_content(selection_error_text)
end end

View file

@ -16,7 +16,7 @@ describe 'Admin::Trends::Tags' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
click_button button_for_allow click_on button_for_allow
expect(page).to have_content(selection_error_text) expect(page).to have_content(selection_error_text)
end end

View file

@ -23,7 +23,7 @@ describe 'email confirmation flow when captcha is enabled' do
expect(user.reload.confirmed?).to be false expect(user.reload.confirmed?).to be false
# It redirects to app and confirms user # It redirects to app and confirms user
click_button I18n.t('challenge.confirm') click_on I18n.t('challenge.confirm')
expect(user.reload.confirmed?).to be true expect(user.reload.confirmed?).to be true
expect(page).to have_current_path(/\A#{client_app.confirmation_redirect_uri}/, url: true) expect(page).to have_current_path(/\A#{client_app.confirmation_redirect_uri}/, url: true)

View file

@ -19,7 +19,7 @@ describe 'Log in' do
it 'A valid email and password user is able to log in' do it 'A valid email and password user is able to log in' do
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: password fill_in 'user_password', with: password
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(subject).to have_css('div.app-holder') expect(subject).to have_css('div.app-holder')
end end
@ -27,7 +27,7 @@ describe 'Log in' do
it 'A invalid email and password user is not able to log in' do it 'A invalid email and password user is not able to log in' do
fill_in 'user_email', with: 'invalid_email' fill_in 'user_email', with: 'invalid_email'
fill_in 'user_password', with: 'invalid_password' fill_in 'user_password', with: 'invalid_password'
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(subject).to have_css('.flash-message', text: failure_message('invalid')) expect(subject).to have_css('.flash-message', text: failure_message('invalid'))
end end
@ -38,7 +38,7 @@ describe 'Log in' do
it 'A unconfirmed user is able to log in' do it 'A unconfirmed user is able to log in' do
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: password fill_in 'user_password', with: password
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(subject).to have_css('div.admin-wrapper') expect(subject).to have_css('div.admin-wrapper')
end end

View file

@ -20,7 +20,7 @@ describe 'Using OAuth from an external app' do
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize'))
# Upon authorizing, it redirects to the apps' callback URL # Upon authorizing, it redirects to the apps' callback URL
click_button I18n.t('doorkeeper.authorizations.buttons.authorize') click_on I18n.t('doorkeeper.authorizations.buttons.authorize')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It grants the app access to the account # It grants the app access to the account
@ -35,7 +35,7 @@ describe 'Using OAuth from an external app' do
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.deny')) expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.deny'))
# Upon denying, it redirects to the apps' callback URL # Upon denying, it redirects to the apps' callback URL
click_button I18n.t('doorkeeper.authorizations.buttons.deny') click_on I18n.t('doorkeeper.authorizations.buttons.deny')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It does not grant the app access to the account # It does not grant the app access to the account
@ -63,17 +63,17 @@ describe 'Using OAuth from an external app' do
# Failing to log-in presents the form again # Failing to log-in presents the form again
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: 'wrong password' fill_in 'user_password', with: 'wrong password'
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('auth.login')) expect(page).to have_content(I18n.t('auth.login'))
# Logging in redirects to an authorization page # Logging in redirects to an authorization page
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: password fill_in 'user_password', with: password
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize'))
# Upon authorizing, it redirects to the apps' callback URL # Upon authorizing, it redirects to the apps' callback URL
click_button I18n.t('doorkeeper.authorizations.buttons.authorize') click_on I18n.t('doorkeeper.authorizations.buttons.authorize')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It grants the app access to the account # It grants the app access to the account
@ -90,17 +90,17 @@ describe 'Using OAuth from an external app' do
# Failing to log-in presents the form again # Failing to log-in presents the form again
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: 'wrong password' fill_in 'user_password', with: 'wrong password'
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('auth.login')) expect(page).to have_content(I18n.t('auth.login'))
# Logging in redirects to an authorization page # Logging in redirects to an authorization page
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: password fill_in 'user_password', with: password
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize'))
# Upon denying, it redirects to the apps' callback URL # Upon denying, it redirects to the apps' callback URL
click_button I18n.t('doorkeeper.authorizations.buttons.deny') click_on I18n.t('doorkeeper.authorizations.buttons.deny')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It does not grant the app access to the account # It does not grant the app access to the account
@ -120,27 +120,27 @@ describe 'Using OAuth from an external app' do
# Failing to log-in presents the form again # Failing to log-in presents the form again
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: 'wrong password' fill_in 'user_password', with: 'wrong password'
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('auth.login')) expect(page).to have_content(I18n.t('auth.login'))
# Logging in redirects to a two-factor authentication page # Logging in redirects to a two-factor authentication page
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: password fill_in 'user_password', with: password
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp')) expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp'))
# Filling in an incorrect two-factor authentication code presents the form again # Filling in an incorrect two-factor authentication code presents the form again
fill_in 'user_otp_attempt', with: 'wrong' fill_in 'user_otp_attempt', with: 'wrong'
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp')) expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp'))
# Filling in the correct TOTP code redirects to an app authorization page # Filling in the correct TOTP code redirects to an app authorization page
fill_in 'user_otp_attempt', with: user.current_otp fill_in 'user_otp_attempt', with: user.current_otp
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize'))
# Upon authorizing, it redirects to the apps' callback URL # Upon authorizing, it redirects to the apps' callback URL
click_button I18n.t('doorkeeper.authorizations.buttons.authorize') click_on I18n.t('doorkeeper.authorizations.buttons.authorize')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It grants the app access to the account # It grants the app access to the account
@ -157,27 +157,27 @@ describe 'Using OAuth from an external app' do
# Failing to log-in presents the form again # Failing to log-in presents the form again
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: 'wrong password' fill_in 'user_password', with: 'wrong password'
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('auth.login')) expect(page).to have_content(I18n.t('auth.login'))
# Logging in redirects to a two-factor authentication page # Logging in redirects to a two-factor authentication page
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: password fill_in 'user_password', with: password
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp')) expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp'))
# Filling in an incorrect two-factor authentication code presents the form again # Filling in an incorrect two-factor authentication code presents the form again
fill_in 'user_otp_attempt', with: 'wrong' fill_in 'user_otp_attempt', with: 'wrong'
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp')) expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp'))
# Filling in the correct TOTP code redirects to an app authorization page # Filling in the correct TOTP code redirects to an app authorization page
fill_in 'user_otp_attempt', with: user.current_otp fill_in 'user_otp_attempt', with: user.current_otp
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize'))
# Upon denying, it redirects to the apps' callback URL # Upon denying, it redirects to the apps' callback URL
click_button I18n.t('doorkeeper.authorizations.buttons.deny') click_on I18n.t('doorkeeper.authorizations.buttons.deny')
expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true)
# It does not grant the app access to the account # It does not grant the app access to the account

View file

@ -18,7 +18,7 @@ module ProfileStories
visit new_user_session_path visit new_user_session_path
fill_in 'user_email', with: email fill_in 'user_email', with: email
fill_in 'user_password', with: password fill_in 'user_password', with: password
click_button I18n.t('auth.login') click_on I18n.t('auth.login')
end end
def with_alice_as_local_user def with_alice_as_local_user

View file

@ -24,7 +24,7 @@ describe 'NewStatuses' do
within('.compose-form') do within('.compose-form') do
fill_in "What's on your mind?", with: status_text fill_in "What's on your mind?", with: status_text
click_button 'Publish!' click_on 'Publish!'
end end
expect(subject).to have_css('.status__content__text', text: status_text) expect(subject).to have_css('.status__content__text', text: status_text)
@ -37,7 +37,7 @@ describe 'NewStatuses' do
within('.compose-form') do within('.compose-form') do
fill_in "What's on your mind?", with: status_text fill_in "What's on your mind?", with: status_text
click_button 'Publish!' click_on 'Publish!'
end end
expect(subject).to have_css('.status__content__text', text: status_text) expect(subject).to have_css('.status__content__text', text: status_text)