From 4df6bb0a52cdfc0c924bd289e0ce3e8d77dd9ed7 Mon Sep 17 00:00:00 2001
From: Chris Huynh <82407232+ChrisHuynh333@users.noreply.github.com>
Date: Tue, 31 Dec 2024 09:53:08 -0600
Subject: [PATCH] Data Table Bot Accounts: Remove PAT panel on destroy (#860)
* some cleanup, fix some tests
* fix translations
* refactor PAT tables into component
* finish refactoring translations
* rework logic to handle closing PAT panel upon destroy bot or revoke PAT
* cleanup, add tests
* cleanup translations
* remove whitespace for some styling classes
* cleanup tests
* further cleanup tests
* fix selector
* attempt to fix flakes
* run normalize
* remove commented out code
* cleanup tests, add unauthorized test cases
* change new_destroy and new_revoke to destroy_confirmation and revoke_confirmation
* run normalize
---
app/components/bots/table_component.html.erb | 4 +-
app/components/bots/table_component.rb | 4 +-
.../table_component.html.erb | 5 +-
.../personal_access_tokens/table_component.rb | 15 +-
app/controllers/concerns/bot_actions.rb | 15 +-
.../bot_personal_access_token_actions.rb | 13 +-
.../controllers/token_controller.js | 9 +-
.../bots/_destroy_confirmation_modal.html.erb | 31 +++
app/views/groups/bots/index.html.erb | 1 +
.../_revoke_confirmation_modal.html.erb | 38 ++++
.../bots/_destroy_confirmation_modal.html.erb | 31 +++
app/views/projects/bots/index.html.erb | 1 +
.../_revoke_confirmation_modal.html.erb | 38 ++++
config/locales/en.yml | 9 +-
config/locales/fr.yml | 9 +-
config/routes/group.rb | 2 +
config/routes/project.rb | 2 +
.../concerns/bot_actions_concern_test.rb | 112 +++++------
...sonal_access_token_actions_concern_test.rb | 50 +++++
test/system/groups/bots_test.rb | 169 +++++++++++++----
test/system/projects/bots_test.rb | 179 ++++++++++++++----
21 files changed, 580 insertions(+), 157 deletions(-)
create mode 100644 app/views/groups/bots/_destroy_confirmation_modal.html.erb
create mode 100644 app/views/groups/bots/personal_access_tokens/_revoke_confirmation_modal.html.erb
create mode 100644 app/views/projects/bots/_destroy_confirmation_modal.html.erb
create mode 100644 app/views/projects/bots/personal_access_tokens/_revoke_confirmation_modal.html.erb
diff --git a/app/components/bots/table_component.html.erb b/app/components/bots/table_component.html.erb
index ab63e6c814..0edc6697c6 100644
--- a/app/components/bots/table_component.html.erb
+++ b/app/components/bots/table_component.html.erb
@@ -52,9 +52,7 @@
t("bots.index.table.actions.destroy"),
destroy_path(row),
data: {
- turbo_method: :delete,
- turbo_confirm: t("bots.index.table.actions.destroy_confirmation"),
- turbo_frame: "_top",
+ "turbo-stream": true,
},
aria: {
label: t("bots.index.table.actions.destroy_aria_label"),
diff --git a/app/components/bots/table_component.rb b/app/components/bots/table_component.rb
index b477c2764c..cd970ea75a 100644
--- a/app/components/bots/table_component.rb
+++ b/app/components/bots/table_component.rb
@@ -45,9 +45,9 @@ def new_token_path(bot)
def destroy_path(bot)
if @namespace.is_a?(Group)
- group_bot_path(id: bot.id)
+ group_bot_destroy_confirmation_path(bot_id: bot.id)
elsif @namespace.is_a?(Namespaces::ProjectNamespace)
- namespace_project_bot_path(id: bot.id)
+ namespace_project_bot_destroy_confirmation_path(bot_id: bot.id)
end
end
end
diff --git a/app/components/personal_access_tokens/table_component.html.erb b/app/components/personal_access_tokens/table_component.html.erb
index c7dc96af69..a556ca4ec1 100644
--- a/app/components/personal_access_tokens/table_component.html.erb
+++ b/app/components/personal_access_tokens/table_component.html.erb
@@ -29,10 +29,7 @@
<%= link_to(
t("personal_access_tokens.table.revoke"),
revoke_path(row),
- data: {
- turbo_method: :delete,
- turbo_confirm: t("personal_access_tokens.table.revoke_confirmation"),
- },
+ data: revoke_data_attributes,
class:
"font-medium text-blue-600 underline dark:text-blue-500 hover:no-underline cursor-pointer",
) %>
diff --git a/app/components/personal_access_tokens/table_component.rb b/app/components/personal_access_tokens/table_component.rb
index 0812d72389..39675d9362 100644
--- a/app/components/personal_access_tokens/table_component.rb
+++ b/app/components/personal_access_tokens/table_component.rb
@@ -25,12 +25,12 @@ def initialize(
def revoke_path(token)
if @namespace.is_a?(Group)
- revoke_group_bot_personal_access_token_path(
+ revoke_confirmation_group_bot_personal_access_token_path(
bot_id: @bot_account.id,
id: token.id
)
elsif @namespace.is_a?(Namespaces::ProjectNamespace)
- revoke_namespace_project_bot_personal_access_token_path(
+ revoke_confirmation_namespace_project_bot_personal_access_token_path(
bot_id: @bot_account.id,
id: token.id
)
@@ -38,5 +38,16 @@ def revoke_path(token)
revoke_profile_personal_access_token_path(id: token.id)
end
end
+
+ def revoke_data_attributes
+ if @namespace
+ { 'turbo-stream': true }
+ else
+ {
+ turbo_method: :delete,
+ turbo_confirm: t('personal_access_tokens.table.revoke_confirmation')
+ }
+ end
+ end
end
end
diff --git a/app/controllers/concerns/bot_actions.rb b/app/controllers/concerns/bot_actions.rb
index 192a6c97e4..fced0d5f77 100644
--- a/app/controllers/concerns/bot_actions.rb
+++ b/app/controllers/concerns/bot_actions.rb
@@ -7,7 +7,7 @@ module BotActions
included do
before_action proc { namespace }
before_action proc { access_levels }
- before_action proc { bot_account }, only: %i[destroy]
+ before_action proc { bot_account }, only: %i[destroy destroy_confirmation]
before_action proc { bot_type }, only: %i[create]
before_action proc { bot_accounts }
end
@@ -53,6 +53,16 @@ def create # rubocop:disable Metrics/MethodLength
end
end
+ def destroy_confirmation
+ authorize! @namespace, to: :destroy_bot_accounts?
+ render turbo_stream: turbo_stream.update('bot_modal',
+ partial: 'destroy_confirmation_modal',
+ locals: {
+ open: true,
+ bot_account: @bot_account
+ }), status: :ok
+ end
+
def destroy
Bots::DestroyService.new(@bot_account, current_user).execute
respond_to do |format|
@@ -74,7 +84,8 @@ def destroy
private
def bot_account
- @bot_account = @namespace.namespace_bots.find_by(id: params[:id]) || not_found
+ id = params[:bot_id] || params[:id]
+ @bot_account = @namespace.namespace_bots.find_by(id:) || not_found
end
def access_levels
diff --git a/app/controllers/concerns/bot_personal_access_token_actions.rb b/app/controllers/concerns/bot_personal_access_token_actions.rb
index f0a0267141..1104ca5806 100644
--- a/app/controllers/concerns/bot_personal_access_token_actions.rb
+++ b/app/controllers/concerns/bot_personal_access_token_actions.rb
@@ -8,7 +8,7 @@ module BotPersonalAccessTokenActions
before_action proc { namespace }
before_action proc { bot_account }
before_action proc { personal_access_tokens }, only: %i[index revoke]
- before_action proc { personal_access_token }, only: %i[revoke]
+ before_action proc { personal_access_token }, only: %i[revoke revoke_confirmation]
before_action proc { bot_accounts }
end
@@ -54,9 +54,18 @@ def create # rubocop:disable Metrics/MethodLength
end
end
- def revoke
+ def revoke_confirmation
authorize! @namespace, to: :revoke_bot_personal_access_token?
+ render turbo_stream: turbo_stream.update('token_dialog',
+ partial: 'revoke_confirmation_modal',
+ locals: {
+ open: true,
+ personal_access_token: @personal_access_token,
+ bot_account: @bot_account
+ }), status: :ok
+ end
+ def revoke
respond_to do |format|
format.turbo_stream do
if @personal_access_token.revoke!
diff --git a/app/javascript/controllers/token_controller.js b/app/javascript/controllers/token_controller.js
index 847cc103cd..9db6b63ba7 100644
--- a/app/javascript/controllers/token_controller.js
+++ b/app/javascript/controllers/token_controller.js
@@ -26,7 +26,7 @@ export default class extends Controller {
this.viewTarget.classList.add("hidden");
this.inputTarget.value = Array.prototype.join.call(
{ length: this.itemValue.length },
- "*"
+ "*",
);
} else {
this.hideTarget.classList.add("hidden");
@@ -35,4 +35,11 @@ export default class extends Controller {
}
this.visible = !this.visible;
}
+
+ removeTokenPanel() {
+ let panel = document.getElementById("access-token-section");
+ if (panel) {
+ panel.remove();
+ }
+ }
}
diff --git a/app/views/groups/bots/_destroy_confirmation_modal.html.erb b/app/views/groups/bots/_destroy_confirmation_modal.html.erb
new file mode 100644
index 0000000000..eb7c5a3f00
--- /dev/null
+++ b/app/views/groups/bots/_destroy_confirmation_modal.html.erb
@@ -0,0 +1,31 @@
+<%= viral_dialog(open: open) do |dialog| %>
+ <%= dialog.with_header(title: t("bots.destroy_confirmation.title")) %>
+ <%= dialog.with_section do %>
+
+ <%= turbo_frame_tag("deletion-alert") %>
+
+
+
+ <%= t("bots.destroy_confirmation.description", bot_name: bot_account.user.email) %>
+
+ <%= form_for(:deletion, url: group_bot_path(id: bot_account.id), method: :delete,
+ data: {
+ turbo_frame: "_top",
+ controller: "token",
+ action:"turbo:submit-end->viral--dialog#close"
+ }
+ ) do |form| %>
+ <%= form.submit t("bots.destroy_confirmation.submit_button"),
+ class:
+ "button text-sm px-5 py-2.5 text-white bg-red-700 border-red-800 focus:outline-none hover:bg-red-800 focus:ring-red-300 dark:focus:ring-red-700 dark:bg-red-600 dark:text-white dark:border-red-600 dark:hover:bg-red-700",
+ data: {
+ action: "click->token#removeTokenPanel",
+ } %>
+ <% end %>
+
+ <% end %>
+<% end %>
diff --git a/app/views/groups/bots/index.html.erb b/app/views/groups/bots/index.html.erb
index c7675bf8b9..e340bd0077 100644
--- a/app/views/groups/bots/index.html.erb
+++ b/app/views/groups/bots/index.html.erb
@@ -1,4 +1,5 @@
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>
+<%= turbo_frame_tag "token_dialog" %>
<%= render Viral::PageHeaderComponent.new(title: t('.title'), subtitle: t('.subtitle')) do |component| %>
<%= component.with_icon(name: "users", classes: "h-14 w-14 text-primary-700") %>
<%= component.with_buttons do %>
diff --git a/app/views/groups/bots/personal_access_tokens/_revoke_confirmation_modal.html.erb b/app/views/groups/bots/personal_access_tokens/_revoke_confirmation_modal.html.erb
new file mode 100644
index 0000000000..3ecffe37f7
--- /dev/null
+++ b/app/views/groups/bots/personal_access_tokens/_revoke_confirmation_modal.html.erb
@@ -0,0 +1,38 @@
+<%= viral_dialog(open: open) do |dialog| %>
+ <%= dialog.with_header(title: t("personal_access_tokens.revoke_confirmation.title")) %>
+ <%= dialog.with_section do %>
+
+ <%= turbo_frame_tag("deletion-alert") %>
+
+
+
+ <%= t(
+ "personal_access_tokens.revoke_confirmation.description",
+ token_name: personal_access_token.name,
+ bot_name: bot_account.user.email,
+ ) %>
+
+ <%= form_for(:deletion, url: revoke_group_bot_personal_access_token_path(
+ bot_id: @bot_account.id,
+ id: personal_access_token.id
+ ), method: :delete,
+ data: {
+ turbo_frame: "_top",
+ controller: "token",
+ action:"turbo:submit-end->viral--dialog#close"
+ }
+ ) do |form| %>
+ <%= form.submit t("personal_access_tokens.revoke_confirmation.submit_button"),
+ class:
+ "button text-sm px-5 py-2.5 text-white bg-red-700 border-red-800 focus:outline-none hover:bg-red-800 focus:ring-red-300 dark:focus:ring-red-700 dark:bg-red-600 dark:text-white dark:border-red-600 dark:hover:bg-red-700",
+ data: {
+ action: "click->token#removeTokenPanel",
+ } %>
+ <% end %>
+
+ <% end %>
+<% end %>
diff --git a/app/views/projects/bots/_destroy_confirmation_modal.html.erb b/app/views/projects/bots/_destroy_confirmation_modal.html.erb
new file mode 100644
index 0000000000..e56b3e5c92
--- /dev/null
+++ b/app/views/projects/bots/_destroy_confirmation_modal.html.erb
@@ -0,0 +1,31 @@
+<%= viral_dialog(open: open) do |dialog| %>
+ <%= dialog.with_header(title: t("bots.destroy_confirmation.title")) %>
+ <%= dialog.with_section do %>
+
+ <%= turbo_frame_tag("deletion-alert") %>
+
+
+
+ <%= t("bots.destroy_confirmation.description", bot_name: bot_account.user.email) %>
+
+ <%= form_for(:deletion, url: namespace_project_bot_path(id: bot_account.id), method: :delete,
+ data: {
+ turbo_frame: "_top",
+ controller: "token",
+ action:"turbo:submit-end->viral--dialog#close"
+ }
+ ) do |form| %>
+ <%= form.submit t("bots.destroy_confirmation.submit_button"),
+ class:
+ "button text-sm px-5 py-2.5 text-white bg-red-700 border-red-800 focus:outline-none hover:bg-red-800 focus:ring-red-300 dark:focus:ring-red-700 dark:bg-red-600 dark:text-white dark:border-red-600 dark:hover:bg-red-700",
+ data: {
+ action: "click->token#removeTokenPanel",
+ } %>
+ <% end %>
+
+ <% end %>
+<% end %>
diff --git a/app/views/projects/bots/index.html.erb b/app/views/projects/bots/index.html.erb
index 612d162dda..77273159cd 100644
--- a/app/views/projects/bots/index.html.erb
+++ b/app/views/projects/bots/index.html.erb
@@ -1,4 +1,5 @@
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>
+<%= turbo_frame_tag "token_dialog" %>
<%= render Viral::PageHeaderComponent.new(title: t('.title'), subtitle: t('.subtitle')) do |component| %>
<%= component.with_icon(name: "users", classes: "h-14 w-14 text-primary-700") %>
<% if allowed_to?(:create_bot_accounts?, @namespace) %>
diff --git a/app/views/projects/bots/personal_access_tokens/_revoke_confirmation_modal.html.erb b/app/views/projects/bots/personal_access_tokens/_revoke_confirmation_modal.html.erb
new file mode 100644
index 0000000000..46c39b5fbe
--- /dev/null
+++ b/app/views/projects/bots/personal_access_tokens/_revoke_confirmation_modal.html.erb
@@ -0,0 +1,38 @@
+<%= viral_dialog(open: open) do |dialog| %>
+ <%= dialog.with_header(title: t("personal_access_tokens.revoke_confirmation.title")) %>
+ <%= dialog.with_section do %>
+
+ <%= turbo_frame_tag("deletion-alert") %>
+
+
+
+ <%= t(
+ "personal_access_tokens.revoke_confirmation.description",
+ token_name: personal_access_token.name,
+ bot_name: bot_account.user.email,
+ ) %>
+
+ <%= form_for(:deletion, url: revoke_namespace_project_bot_personal_access_token_path(
+ bot_id: bot_account.id,
+ id: personal_access_token.id
+ ), method: :delete,
+ data: {
+ turbo_frame: "_top",
+ controller: "token",
+ action:"turbo:submit-end->viral--dialog#close"
+ }
+ ) do |form| %>
+ <%= form.submit t("personal_access_tokens.revoke_confirmation.submit_button"),
+ class:
+ "button text-sm px-5 py-2.5 text-white bg-red-700 border-red-800 focus:outline-none hover:bg-red-800 focus:ring-red-300 dark:focus:ring-red-700 dark:bg-red-600 dark:text-white dark:border-red-600 dark:hover:bg-red-700",
+ data: {
+ action: "click->token#removeTokenPanel",
+ } %>
+ <% end %>
+
+ <% end %>
+<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 9ec52159e4..edad40fad6 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -312,6 +312,10 @@ en:
label: Scopes
read_api: Grants read access to the API.
bots:
+ destroy_confirmation:
+ description: This action will permanently delete the bot account and will remove any existing memberships for the bot account. Are you sure you want to permanently delete bot account %{bot_name}?
+ submit_button: Confirm
+ title: Delete Bot
index:
pagy_item: Bot Accounts
table:
@@ -325,7 +329,6 @@ en:
actions:
destroy: Delete
destroy_aria_label: Delete bot account
- destroy_confirmation: This action will permanently delete the bot account and will remove any existing memberships for the bot account. Are you sure you want to permanently delete this bot account?
generate_new_token: Generate new token
generate_new_token_aria_label: Generate a new personal access token for bot account
empty_state:
@@ -944,6 +947,10 @@ en:
helper: A custom name will make it easier to search for this in the future.
label: Name (Optional)
personal_access_tokens:
+ revoke_confirmation:
+ description: Are you sure you'd like to remove access token '%{token_name}' from bot %{bot_name}?
+ submit_button: Confirm
+ title: Revoke personal access token
table:
header:
action: Action
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 9c9cda4601..7ac0599d10 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -312,6 +312,10 @@ fr:
label: Scopes
read_api: Grants read access to the API.
bots:
+ destroy_confirmation:
+ description: This action will permanently delete the bot account and will remove any existing memberships for the bot account. Are you sure you want to permanently delete bot account %{bot_name}?
+ submit_button: Confirm
+ title: Delete Bot
index:
pagy_item: Bot Accounts
table:
@@ -325,7 +329,6 @@ fr:
actions:
destroy: Delete
destroy_aria_label: Delete bot account
- destroy_confirmation: This action will permanently delete the bot account and will remove any existing memberships for the bot account. Are you sure you want to permanently delete this bot account?
generate_new_token: Generate new token
generate_new_token_aria_label: Generate a new personal access token for bot account
empty_state:
@@ -944,6 +947,10 @@ fr:
helper: A custom name will make it easier to search for this in the future.
label: Name (Optional)
personal_access_tokens:
+ revoke_confirmation:
+ description: Are you sure you'd like to remove access token '%{token_name}' from bot %{bot_name}?
+ submit_button: Confirm
+ title: Revoke personal access token
table:
header:
action: Action
diff --git a/config/routes/group.rb b/config/routes/group.rb
index dfa60aab2b..63a9f6bd1a 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -18,8 +18,10 @@
resources :members, only: %i[create destroy index new update]
resources :bots, only: %i[create destroy index new] do
+ get :destroy_confirmation
resources :personal_access_tokens, module: :bots, only: %i[index new create] do
member do
+ get :revoke_confirmation
delete :revoke
end
end
diff --git a/config/routes/project.rb b/config/routes/project.rb
index d329342ed2..f44387020d 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -23,9 +23,11 @@
end
resources :bots, only: %i[create destroy index new] do
+ get :destroy_confirmation
resources :personal_access_tokens, module: :bots, only: %i[index new create] do
member do
delete :revoke
+ get :revoke_confirmation
end
end
end
diff --git a/test/controllers/concerns/bot_actions_concern_test.rb b/test/controllers/concerns/bot_actions_concern_test.rb
index 1711d54741..e5aa9774fe 100644
--- a/test/controllers/concerns/bot_actions_concern_test.rb
+++ b/test/controllers/concerns/bot_actions_concern_test.rb
@@ -5,35 +5,28 @@
class BotActionsConcernTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
- test 'project bot accounts index' do
+ setup do
sign_in users(:john_doe)
+ @namespace = groups(:group_one)
+ @project = projects(:project1)
+ @project_bot = namespace_bots(:project1_bot0)
+ @group_bot = namespace_bots(:group1_bot0)
+ end
- namespace = groups(:group_one)
- project = projects(:project1)
-
- get namespace_project_bots_path(namespace, project)
+ test 'project bot accounts index' do
+ get namespace_project_bots_path(@namespace, @project)
assert_response :success
end
test 'new project bot account' do
- sign_in users(:john_doe)
-
- namespace = groups(:group_one)
- project = projects(:project1)
-
- get new_namespace_project_bot_path(namespace, project, format: :turbo_stream)
+ get new_namespace_project_bot_path(@namespace, @project, format: :turbo_stream)
assert_response :success
end
test 'project bot account create' do
- sign_in users(:john_doe)
-
- namespace = groups(:group_one)
- project = projects(:project1)
-
- post namespace_project_bots_path(namespace, project, format: :turbo_stream),
+ post namespace_project_bots_path(@namespace, @project, format: :turbo_stream),
params: { bot: {
token_name: 'newtesttoken',
access_level: Member::AccessLevel::UPLOADER,
@@ -44,12 +37,7 @@ class BotActionsConcernTest < ActionDispatch::IntegrationTest
end
test 'project bot account create error' do
- sign_in users(:john_doe)
-
- namespace = groups(:group_one)
- project = projects(:project1)
-
- post namespace_project_bots_path(namespace, project, format: :turbo_stream),
+ post namespace_project_bots_path(@namespace, @project, format: :turbo_stream),
params: { bot: {
access_level: Member::AccessLevel::UPLOADER,
scopes: ['read_api']
@@ -59,55 +47,33 @@ class BotActionsConcernTest < ActionDispatch::IntegrationTest
end
test 'project bot account destroy' do
- sign_in users(:john_doe)
-
- namespace_bot = namespace_bots(:project1_bot0)
-
- namespace = groups(:group_one)
- project = projects(:project1)
-
- delete namespace_project_bot_path(namespace, project, id: namespace_bot.id, format: :turbo_stream)
+ delete namespace_project_bot_path(@namespace, @project, id: @project_bot.id, format: :turbo_stream)
assert_response :redirect
end
test 'project bot account destroy error' do
- sign_in users(:john_doe)
-
- namespace = groups(:group_one)
- project = projects(:project2)
+ project2 = projects(:project2)
- delete namespace_project_bot_path(namespace, project, id: 0, format: :turbo_stream)
+ delete namespace_project_bot_path(@namespace, project2, id: 0, format: :turbo_stream)
assert_response :not_found
end
test 'group bot accounts index' do
- sign_in users(:john_doe)
-
- namespace = groups(:group_one)
-
- get group_bots_path(namespace)
+ get group_bots_path(@namespace)
assert_response :success
end
test 'new group bot account' do
- sign_in users(:john_doe)
-
- namespace = groups(:group_one)
-
- get new_group_bot_path(namespace, format: :turbo_stream)
+ get new_group_bot_path(@namespace, format: :turbo_stream)
assert_response :success
end
test 'group bot account create' do
- sign_in users(:john_doe)
-
- namespace = groups(:group_one)
-
- post group_bots_path(namespace, format: :turbo_stream),
+ post group_bots_path(@namespace, format: :turbo_stream),
params: { bot: {
token_name: 'newtesttoken',
access_level: Member::AccessLevel::UPLOADER,
@@ -118,11 +84,7 @@ class BotActionsConcernTest < ActionDispatch::IntegrationTest
end
test 'group bot account create error' do
- sign_in users(:john_doe)
-
- namespace = groups(:group_one)
-
- post group_bots_path(namespace, format: :turbo_stream),
+ post group_bots_path(@namespace, format: :turbo_stream),
params: { bot: {
access_level: Member::AccessLevel::UPLOADER,
scopes: ['read_api']
@@ -132,24 +94,42 @@ class BotActionsConcernTest < ActionDispatch::IntegrationTest
end
test 'group bot account destroy' do
- sign_in users(:john_doe)
+ delete group_bot_path(@namespace, id: @group_bot.id, format: :turbo_stream)
- namespace_bot = namespace_bots(:group1_bot0)
+ assert_response :redirect
+ end
- namespace = groups(:group_one)
+ test 'group bot account destroy error' do
+ delete group_bot_path(@namespace, id: 0, format: :turbo_stream)
- delete group_bot_path(namespace, id: namespace_bot.id, format: :turbo_stream)
+ assert_response :not_found
+ end
- assert_response :redirect
+ test 'destroy_confirmation in group' do
+ get group_bot_destroy_confirmation_path(@namespace, bot_id: @group_bot.id)
+
+ assert_response :success
end
- test 'group bot account destroy error' do
- sign_in users(:john_doe)
+ test 'unauthorized destroy_confirmation in group' do
+ sign_in users(:ryan_doe)
- namespace = groups(:group_one)
+ get group_bot_destroy_confirmation_path(@namespace, bot_id: @group_bot.id)
- delete group_bot_path(namespace, id: 0, format: :turbo_stream)
+ assert_response :unauthorized
+ end
- assert_response :not_found
+ test 'destroy_confirmation in project' do
+ get namespace_project_bot_destroy_confirmation_path(@namespace, @project, bot_id: @project_bot.id)
+
+ assert_response :success
+ end
+
+ test 'unauthorized destroy_confirmation in project' do
+ sign_in users(:ryan_doe)
+
+ get namespace_project_bot_destroy_confirmation_path(@namespace, @project, bot_id: @project_bot.id)
+
+ assert_response :unauthorized
end
end
diff --git a/test/controllers/concerns/bot_personal_access_token_actions_concern_test.rb b/test/controllers/concerns/bot_personal_access_token_actions_concern_test.rb
index 8f5cd21420..8b41650daf 100644
--- a/test/controllers/concerns/bot_personal_access_token_actions_concern_test.rb
+++ b/test/controllers/concerns/bot_personal_access_token_actions_concern_test.rb
@@ -244,4 +244,54 @@ class BotPersonalAcessTokenActionsConcernTest < ActionDispatch::IntegrationTest
assert_response :unprocessable_entity
end
+
+ test 'can open new revoke in group' do
+ sign_in users(:john_doe)
+
+ namespace = groups(:group_one)
+
+ namespace_bot = namespace_bots(:group1_bot0)
+ token = personal_access_tokens(:user_group_bot_account0_valid_pat)
+ get revoke_confirmation_group_bot_personal_access_token_path(
+ namespace,
+ bot_id: namespace_bot.id,
+ id: token.id
+ )
+
+ assert_response :success
+ end
+
+ test 'cannot open new revoke in group due to permissions' do
+ sign_in users(:micha_doe)
+
+ namespace = groups(:group_one)
+ namespace_bot = namespace_bots(:group1_bot0)
+ token = personal_access_tokens(:user_group_bot_account0_valid_pat)
+
+ get revoke_confirmation_group_bot_personal_access_token_path(
+ namespace,
+ bot_id: namespace_bot.id,
+ id: token.id
+ )
+
+ assert_response :unauthorized
+ end
+
+ test 'can open new revoke in project' do
+ sign_in users(:john_doe)
+
+ namespace = groups(:group_one)
+ project = projects(:project1)
+
+ namespace_bot = namespace_bots(:project1_bot0)
+ token = personal_access_tokens(:user_bot_account0_valid_pat)
+
+ get revoke_confirmation_namespace_project_bot_personal_access_token_path(
+ namespace,
+ project,
+ bot_id: namespace_bot.id,
+ id: token.id
+ )
+ assert_response :success
+ end
end
diff --git a/test/system/groups/bots_test.rb b/test/system/groups/bots_test.rb
index 8e707fb905..279f24b09d 100644
--- a/test/system/groups/bots_test.rb
+++ b/test/system/groups/bots_test.rb
@@ -7,9 +7,10 @@ class BotsTest < ApplicationSystemTestCase
header_row_count = 1
def setup
- @user = users(:john_doe)
- login_as @user
+ login_as users(:john_doe)
@namespace = groups(:group_one)
+ @group_bot = namespace_bots(:group1_bot0)
+ @group_bot_active_tokens = @group_bot.user.personal_access_tokens.active
end
test 'can see a table listing of group bot accounts' do
@@ -72,7 +73,7 @@ def setup
assert_selector 'h1', text: I18n.t(:'groups.bots.index.bot_listing.new_bot_modal.title')
assert_selector 'p', text: I18n.t(:'groups.bots.index.bot_listing.new_bot_modal.description')
- fill_in 'Token Name', with: 'Uploader'
+ fill_in I18n.t('groups.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Uploader'
find('#bot_access_level').find('option',
text: I18n.t('activerecord.models.member.access_level.analyst')).select_option
@@ -115,7 +116,7 @@ def setup
assert_selector 'h1', text: I18n.t(:'groups.bots.index.bot_listing.new_bot_modal.title')
assert_selector 'p', text: I18n.t(:'groups.bots.index.bot_listing.new_bot_modal.description')
- fill_in 'Token Name', with: 'Uploader'
+ fill_in I18n.t('groups.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Uploader'
find('#bot_access_level').find('option',
text: I18n.t('activerecord.models.member.access_level.analyst')).select_option
@@ -140,8 +141,8 @@ def setup
end
end
- within('#turbo-confirm[open]') do
- click_button 'Confirm'
+ within('dialog') do
+ click_button I18n.t('bots.destroy_confirmation.submit_button')
end
assert_text I18n.t(:'concerns.bot_actions.destroy.success')
@@ -151,15 +152,12 @@ def setup
end
test 'can view personal access tokens for bot account' do
- namespace_bot = namespace_bots(:group1_bot0)
- active_personal_tokens = namespace_bot.user.personal_access_tokens.active
-
visit group_bots_path(@namespace)
assert_selector 'h1', text: I18n.t(:'groups.bots.index.title')
assert_selector 'p', text: I18n.t(:'groups.bots.index.subtitle')
- within "tr[id='#{namespace_bot.id}']" do
- click_link active_personal_tokens.count.to_s
+ within "tr[id='#{@group_bot.id}']" do
+ click_link @group_bot_active_tokens.count.to_s
end
within('dialog') do
@@ -167,12 +165,12 @@ def setup
assert_selector 'p',
text: I18n.t(
'groups.bots.index.personal_access_tokens_listing_modal.description',
- bot_account: namespace_bot.user.email
+ bot_account: @group_bot.user.email
)
within('table') do
assert_selector 'tr', count: 2
- token = active_personal_tokens.first
+ token = @group_bot_active_tokens.first
within "tr[id='#{token.id}']" do
assert_equal 'Valid PAT0', token.name
@@ -195,13 +193,11 @@ def setup
end
test 'can generate a new personal access token for bot account' do
- namespace_bot = namespace_bots(:group1_bot0)
-
visit group_bots_path(@namespace)
assert_selector 'h1', text: I18n.t(:'groups.bots.index.title')
assert_selector 'p', text: I18n.t(:'groups.bots.index.subtitle')
- within "tr[id='#{namespace_bot.id}']" do
+ within "tr[id='#{@group_bot.id}']" do
click_link 'Generate new token'
end
@@ -211,9 +207,9 @@ def setup
)
assert_text I18n.t('groups.bots.index.bot_listing.generate_personal_access_token_modal.description',
- bot_account: namespace_bot.user.email)
+ bot_account: @group_bot.user.email)
- fill_in 'Token Name', with: 'Newest token'
+ fill_in I18n.t('groups.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Newest token'
all('input[type=checkbox]').each(&:click)
@@ -221,24 +217,21 @@ def setup
end
within('#access-token-section') do
- bot_account_name = namespace_bot.user.email
- assert_selector 'h2', text: I18n.t('groups.bots.index.access_token_section.label', bot_name: bot_account_name)
+ assert_selector 'h2', text: I18n.t('groups.bots.index.access_token_section.label',
+ bot_name: @group_bot.user.email)
assert_selector 'p', text: I18n.t('groups.bots.index.access_token_section.description')
assert_selector 'button', text: I18n.t('components.token.copy')
end
end
test 'can revoke a personal access token' do
- namespace_bot = namespace_bots(:group1_bot0)
- active_personal_tokens = namespace_bot.user.personal_access_tokens.active
- token = nil
-
+ token = @group_bot_active_tokens.first
visit group_bots_path(@namespace)
assert_selector 'h1', text: I18n.t(:'groups.bots.index.title')
assert_selector 'p', text: I18n.t(:'groups.bots.index.subtitle')
- within "tr[id='#{namespace_bot.id}']" do
- click_link active_personal_tokens.count.to_s
+ within "tr[id='#{@group_bot.id}']" do
+ click_link @group_bot_active_tokens.count.to_s
end
within('dialog') do
@@ -246,28 +239,138 @@ def setup
assert_selector 'p',
text: I18n.t(
'groups.bots.index.personal_access_tokens_listing_modal.description',
- bot_account: namespace_bot.user.email
+ bot_account: @group_bot.user.email
)
within('table') do
assert_selector 'tr', count: 2
- token = active_personal_tokens.first
within "tr[id='#{token.id}']" do
- click_link 'Revoke'
+ click_link I18n.t('personal_access_tokens.table.revoke')
end
end
end
- within('#turbo-confirm[open]') do
- click_button 'Confirm'
- end
-
within('dialog') do
+ click_button I18n.t('personal_access_tokens.revoke_confirmation.submit_button')
within('#personal-access-token-alert') do
assert_text I18n.t('concerns.bot_personal_access_token_actions.revoke.success', pat_name: token.name)
end
end
end
+
+ test 'PAT panel removed after personal access token revoke' do
+ ### SETUP START ###
+
+ visit group_bots_path(@namespace)
+ # PAT panel is not present
+ assert_no_selector '#access-token-section div'
+ # create new PAT to render PAT panel
+ within "tr[id='#{@group_bot.id}']" do
+ click_link I18n.t('bots.index.table.actions.generate_new_token')
+ end
+
+ within('#dialog') do
+ assert_text I18n.t(
+ 'groups.bots.index.bot_listing.generate_personal_access_token_modal.title'
+ )
+
+ assert_text I18n.t('groups.bots.index.bot_listing.generate_personal_access_token_modal.description',
+ bot_account: @group_bot.user.email)
+
+ fill_in I18n.t('groups.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Newest token'
+
+ all('input[type=checkbox]').each(&:click)
+
+ click_button I18n.t('groups.bots.index.bot_listing.generate_personal_access_token_modal.submit')
+ end
+
+ # PAT panel now present
+ assert_selector '#access-token-section div'
+ # additional asserts to prevent flakes
+ within('#access-token-section') do
+ assert_text I18n.t('groups.bots.index.access_token_section.label', bot_name: @group_bot.user.email)
+ assert_text I18n.t('groups.bots.index.access_token_section.description')
+ end
+ ### SETUP END ###
+
+ ### ACTIONS START ###
+ # additional asserts to prevent flakes
+ assert_selector '#bots-table'
+ assert_selector '#bots-table table tbody tr', count: 20
+ within "tr[id='#{@group_bot.id}']" do
+ # click active tokens number
+ click_link @group_bot_active_tokens.count.to_s
+ end
+
+ # bot's current PATs dialog
+ within('#dialog') do
+ # revoke a PAT
+ within("table tbody tr[id='#{@group_bot_active_tokens.first.id}']") do
+ click_link I18n.t('personal_access_tokens.table.revoke')
+ end
+
+ click_button I18n.t('personal_access_tokens.revoke_confirmation.submit_button')
+ end
+ ### ACTIONS END ###
+
+ ### VERIFY START ###
+ # PAT panel no longer present
+ assert_no_selector '#access-token-section div'
+ ### VERIFY END ###
+ end
+
+ test 'PAT panel removed after bot destroy' do
+ ### SETUP START ###
+ visit group_bots_path(@namespace)
+ # PAT panel is not present
+ assert_no_selector '#access-token-section div'
+ # create new PAT to render PAT panel
+ within "tr[id='#{@group_bot.id}']" do
+ click_link I18n.t('bots.index.table.actions.generate_new_token')
+ end
+
+ within('#dialog') do
+ assert_text I18n.t(
+ 'groups.bots.index.bot_listing.generate_personal_access_token_modal.title'
+ )
+
+ assert_text I18n.t('groups.bots.index.bot_listing.generate_personal_access_token_modal.description',
+ bot_account: @group_bot.user.email)
+
+ fill_in I18n.t('groups.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Newest token'
+
+ all('input[type=checkbox]').each(&:click)
+
+ click_button I18n.t('groups.bots.index.bot_listing.generate_personal_access_token_modal.submit')
+ end
+
+ # PAT panel now present
+ assert_selector '#access-token-section div'
+ # additional asserts to prevent flakes
+ within('#access-token-section') do
+ assert_text I18n.t('groups.bots.index.access_token_section.label', bot_name: @group_bot.user.email)
+ assert_text I18n.t('groups.bots.index.access_token_section.description')
+ end
+ ### SETUP END ###
+
+ ### ACTIONS START ###
+ # additional asserts to prevent flakes
+ assert_selector '#bots-table'
+ assert_selector '#bots-table table tbody tr', count: 20
+ within('#bots-table table tbody tr:first-child td:last-child') do
+ # destroy bot
+ click_link I18n.t(:'bots.index.table.actions.destroy')
+ end
+
+ # confirm destroy bot
+ click_button I18n.t('bots.destroy_confirmation.submit_button')
+ ### ACTIONS END ###
+
+ ### VERIFY START ###
+ # PAT panel no longer present
+ assert_no_selector '#access-token-section div'
+ ### VERIFY END ###
+ end
end
end
diff --git a/test/system/projects/bots_test.rb b/test/system/projects/bots_test.rb
index 0a9c5d26b3..d5063a32c1 100644
--- a/test/system/projects/bots_test.rb
+++ b/test/system/projects/bots_test.rb
@@ -7,10 +7,12 @@ class BotsTest < ApplicationSystemTestCase
header_row_count = 1
def setup
- @user = users(:john_doe)
- login_as @user
+ login_as users(:john_doe)
@namespace = groups(:group_one)
@project = projects(:project1)
+ @project2 = projects(:project2)
+ @project_bot = namespace_bots(:project1_bot0)
+ @project_bot_active_tokens = @project_bot.user.personal_access_tokens.active
end
test 'can see a table listing of project bot accounts' do
@@ -35,8 +37,7 @@ def setup
end
test 'can see an empty state for table listing of project bot accounts' do
- project = projects(:project2)
- visit namespace_project_bots_path(@namespace, project)
+ visit namespace_project_bots_path(@namespace, @project2)
assert_selector 'h1', text: I18n.t(:'projects.bots.index.title')
assert_selector 'p', text: I18n.t(:'projects.bots.index.subtitle')
@@ -53,8 +54,7 @@ def setup
end
test 'can create a new project bot account' do
- project = projects(:project2)
- visit namespace_project_bots_path(@namespace, project)
+ visit namespace_project_bots_path(@namespace, @project2)
assert_selector 'h1', text: I18n.t(:'projects.bots.index.title')
assert_selector 'p', text: I18n.t(:'projects.bots.index.subtitle')
@@ -74,7 +74,7 @@ def setup
assert_selector 'h1', text: I18n.t(:'projects.bots.index.bot_listing.new_bot_modal.title')
assert_selector 'p', text: I18n.t(:'projects.bots.index.bot_listing.new_bot_modal.description')
- fill_in 'Token Name', with: 'Uploader'
+ fill_in I18n.t('projects.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Uploader'
find('#bot_access_level').find('option',
text: I18n.t('activerecord.models.member.access_level.analyst')).select_option
@@ -86,7 +86,7 @@ def setup
assert_no_selector 'dialog[open]'
within('#access-token-section') do
- bot_account_name = project.namespace.bots.last.email
+ bot_account_name = @project2.namespace.bots.last.email
assert_selector 'h2', text: I18n.t('projects.bots.index.access_token_section.label', bot_name: bot_account_name)
assert_selector 'p', text: I18n.t('projects.bots.index.access_token_section.description')
assert_selector 'button', text: I18n.t('components.token.copy')
@@ -96,8 +96,7 @@ def setup
end
test 'can\'t create a new project bot account without selecting scopes' do
- project = projects(:project2)
- visit namespace_project_bots_path(@namespace, project)
+ visit namespace_project_bots_path(@namespace, @project2)
assert_selector 'h1', text: I18n.t(:'projects.bots.index.title')
assert_selector 'p', text: I18n.t(:'projects.bots.index.subtitle')
@@ -117,7 +116,7 @@ def setup
assert_selector 'h1', text: I18n.t(:'projects.bots.index.bot_listing.new_bot_modal.title')
assert_selector 'p', text: I18n.t(:'projects.bots.index.bot_listing.new_bot_modal.description')
- fill_in 'Token Name', with: 'Uploader'
+ fill_in I18n.t('projects.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Uploader'
find('#bot_access_level').find('option',
text: I18n.t('activerecord.models.member.access_level.analyst')).select_option
@@ -142,8 +141,8 @@ def setup
end
end
- within('#turbo-confirm[open]') do
- click_button 'Confirm'
+ within('dialog') do
+ click_button I18n.t('bots.destroy_confirmation.submit_button')
end
assert_text I18n.t(:'concerns.bot_actions.destroy.success')
@@ -153,15 +152,14 @@ def setup
end
test 'can view personal access tokens for bot account' do
- namespace_bot = namespace_bots(:project1_bot0)
- active_personal_tokens = namespace_bot.user.personal_access_tokens.active
-
+ token = @project_bot_active_tokens.first
visit namespace_project_bots_path(@namespace, @project)
+
assert_selector 'h1', text: I18n.t(:'projects.bots.index.title')
assert_selector 'p', text: I18n.t(:'projects.bots.index.subtitle')
- within "tr[id='#{namespace_bot.id}']" do
- click_link active_personal_tokens.count.to_s
+ within "tr[id='#{@project_bot.id}']" do
+ click_link @project_bot_active_tokens.count.to_s
end
within('dialog') do
@@ -169,12 +167,11 @@ def setup
assert_selector 'p',
text: I18n.t(
'projects.bots.index.personal_access_tokens_listing_modal.description',
- bot_account: namespace_bot.user.email
+ bot_account: @project_bot.user.email
)
within('table') do
assert_selector 'tr', count: 2
- token = active_personal_tokens.first
within "tr[id='#{token.id}']" do
assert_equal 'Valid PAT0', token.name
@@ -197,13 +194,11 @@ def setup
end
test 'can generate a new personal access token for bot account' do
- namespace_bot = namespace_bots(:project1_bot0)
-
visit namespace_project_bots_path(@namespace, @project)
assert_selector 'h1', text: I18n.t(:'projects.bots.index.title')
assert_selector 'p', text: I18n.t(:'projects.bots.index.subtitle')
- within "tr[id='#{namespace_bot.id}']" do
+ within "tr[id='#{@project_bot.id}']" do
click_link 'Generate new token'
end
@@ -213,9 +208,9 @@ def setup
)
assert_text I18n.t('projects.bots.index.bot_listing.generate_personal_access_token_modal.description',
- bot_account: namespace_bot.user.email)
+ bot_account: @project_bot.user.email)
- fill_in 'Token Name', with: 'Newest token'
+ fill_in I18n.t('projects.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Newest token'
all('input[type=checkbox]').each(&:click)
@@ -223,7 +218,7 @@ def setup
end
within('#access-token-section') do
- bot_account_name = namespace_bot.user.email
+ bot_account_name = @project_bot.user.email
assert_selector 'h2', text: I18n.t('projects.bots.index.access_token_section.label', bot_name: bot_account_name)
assert_selector 'p', text: I18n.t('projects.bots.index.access_token_section.description')
assert_selector 'button', text: I18n.t('components.token.copy')
@@ -231,16 +226,14 @@ def setup
end
test 'can revoke a personal access token' do
- namespace_bot = namespace_bots(:project1_bot0)
- active_personal_tokens = namespace_bot.user.personal_access_tokens.active
- token = nil
-
+ token = @project_bot_active_tokens.first
visit namespace_project_bots_path(@namespace, @project)
+
assert_selector 'h1', text: I18n.t(:'projects.bots.index.title')
assert_selector 'p', text: I18n.t(:'projects.bots.index.subtitle')
- within "tr[id='#{namespace_bot.id}']" do
- click_link active_personal_tokens.count.to_s
+ within "tr[id='#{@project_bot.id}']" do
+ click_link @project_bot_active_tokens.count.to_s
end
within('dialog') do
@@ -248,28 +241,134 @@ def setup
assert_selector 'p',
text: I18n.t(
'projects.bots.index.personal_access_tokens_listing_modal.description',
- bot_account: namespace_bot.user.email
+ bot_account: @project_bot.user.email
)
within('table') do
assert_selector 'tr', count: 2
- token = active_personal_tokens.first
-
within "tr[id='#{token.id}']" do
click_link 'Revoke'
end
end
end
- within('#turbo-confirm[open]') do
- click_button 'Confirm'
+ click_button I18n.t('personal_access_tokens.revoke_confirmation.submit_button')
+ within('#personal-access-token-alert') do
+ assert_text I18n.t('concerns.bot_personal_access_token_actions.revoke.success', pat_name: token.name)
end
+ end
- within('dialog') do
- within('#personal-access-token-alert') do
- assert_text I18n.t('concerns.bot_personal_access_token_actions.revoke.success', pat_name: token.name)
+ test 'PAT panel removed after personal access token revoke' do
+ ### SETUP START ###
+ visit namespace_project_bots_path(@namespace, @project)
+ # PAT panel is not present
+ assert_no_selector '#access-token-section div'
+ # create new PAT to render PAT panel
+ within "tr[id='#{@project_bot.id}']" do
+ click_link I18n.t('bots.index.table.actions.generate_new_token')
+ end
+
+ within('#dialog') do
+ assert_text I18n.t(
+ 'projects.bots.index.bot_listing.generate_personal_access_token_modal.title'
+ )
+
+ assert_text I18n.t('projects.bots.index.bot_listing.generate_personal_access_token_modal.description',
+ bot_account: @project_bot.user.email)
+
+ fill_in I18n.t('projects.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Newest token'
+
+ all('input[type=checkbox]').each(&:click)
+
+ click_button I18n.t('projects.bots.index.bot_listing.generate_personal_access_token_modal.submit')
+ end
+
+ # PAT panel now present
+ # additional asserts to prevent flakes
+ assert_selector '#access-token-section div'
+ within('#access-token-section') do
+ assert_text I18n.t('projects.bots.index.access_token_section.label', bot_name: @project_bot.user.email)
+ assert_text I18n.t('projects.bots.index.access_token_section.description')
+ end
+ ### SETUP END ###
+
+ ### ACTIONS START ###
+ # additional asserts to prevent flakes
+ assert_selector '#bots-table'
+ assert_selector '#bots-table table tbody tr', count: 20
+ within "tr[id='#{@project_bot.id}']" do
+ # click active tokens number
+ click_link @project_bot_active_tokens.count.to_s
+ end
+
+ # bot's current PATs dialog
+ within('#dialog') do
+ # revoke a PAT
+ within("table tbody tr[id='#{@project_bot_active_tokens.first.id}']") do
+ click_link I18n.t('personal_access_tokens.table.revoke')
end
+
+ click_button I18n.t('personal_access_tokens.revoke_confirmation.submit_button')
+ end
+ ### ACTIONS END ###
+
+ ### VERIFY START ###
+ # PAT panel no longer present
+ assert_no_selector '#access-token-section div'
+ ### VERIFY END ###
+ end
+
+ test 'PAT panel removed after bot destroy' do
+ ### SETUP START ###
+ visit namespace_project_bots_path(@namespace, @project)
+ # PAT panel is not present
+ assert_no_selector '#access-token-section div'
+ # create new PAT to render PAT panel
+ within "tr[id='#{@project_bot.id}']" do
+ click_link I18n.t('bots.index.table.actions.generate_new_token')
+ end
+
+ within('#dialog') do
+ assert_text I18n.t(
+ 'projects.bots.index.bot_listing.generate_personal_access_token_modal.title'
+ )
+
+ assert_text I18n.t('projects.bots.index.bot_listing.generate_personal_access_token_modal.description',
+ bot_account: @project_bot.user.email)
+
+ fill_in I18n.t('projects.bots.index.bot_listing.new_bot_modal.token_name'), with: 'Newest token'
+
+ all('input[type=checkbox]').each(&:click)
+
+ click_button I18n.t('projects.bots.index.bot_listing.generate_personal_access_token_modal.submit')
end
+
+ # PAT panel now present
+ assert_selector '#access-token-section div'
+ # additional asserts to prevent flakes
+ within('#access-token-section') do
+ assert_text I18n.t('projects.bots.index.access_token_section.label', bot_name: @project_bot.user.email)
+ assert_text I18n.t('projects.bots.index.access_token_section.description')
+ end
+ ### SETUP END ###
+
+ ### ACTIONS START ###
+ # additional asserts to prevent flakes
+ assert_selector '#bots-table'
+ assert_selector '#bots-table table tbody tr', count: 20
+ within('#bots-table table tbody tr:first-child td:last-child') do
+ # destroy bot
+ click_link I18n.t(:'bots.index.table.actions.destroy')
+ end
+
+ # confirm destroy bot
+ click_button I18n.t('bots.destroy_confirmation.submit_button')
+ ### ACTIONS END ###
+
+ ### VERIFY START ###
+ # PAT panel no longer present
+ assert_no_selector '#access-token-section div'
+ ### VERIFY END ###
end
end
end