Skip to content

Commit

Permalink
Add temporary workflow to test e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mszostok committed Jun 18, 2024
1 parent ee243b3 commit 0fa00e0
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 44 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/test-ui-e2e-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Test UI E2E tests on PR

on:
push:
branches:
- extend-ui-tests

env:
GIT_USER: botkube-dev
HELM_VERSION: v3.9.0
K3D_VERSION: v5.4.6
IMAGE_REGISTRY: "ghcr.io"
IMAGE_REPOSITORY: "kubeshop/botkube"
IMAGE_TAG: v9.99.9-dev # TODO: Use commit hash tag to make the predictable builds for each commit on branch

jobs:
cloud-slack-dev-e2e:
name: Botkube Cloud Slack Dev E2E
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
concurrency:
group: cloud-slack-dev-e2e
cancel-in-progress: false
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Run e2e tests
uses: ./.github/actions/cloud-slack-e2e
with:
access_token: ${{ secrets.E2E_TEST_GH_DEV_ACCOUNT_PAT }}

slack_workspace_name: ${{ secrets.E2E_DEV_SLACK_WORKSPACE_NAME }}
slack_email: ${{ secrets.E2E_DEV_SLACK_EMAIL }}
slack_password: ${{ secrets.E2E_DEV_SLACK_USER_PASSWORD }}
slack_bot_display_name: "BotkubeDev"
slack_tester_bot_token: ${{ secrets.E2E_DEV_SLACK_TESTER_BOT_TOKEN }}
slack_tester_bot_name: "botkubedev"

botkube_cloud_api_base_url: "https://api-dev.botkube.io"
botkube_cloud_email: ${{ secrets.E2E_DEV_BOTKUBE_CLOUD_EMAIL }}
botkube_cloud_password: ${{ secrets.E2E_DEV_BOTKUBE_CLOUD_PASSWORD }}
botkube_cloud_team_organization_id: ${{ secrets.E2E_DEV_BOTKUBE_CLOUD_TEAM_ORGANIZATION_ID }}

slack_alerts_webhook: ${{ secrets.SLACK_CI_ALERTS_WEBHOOK }}

e2e_type: "DEV"
44 changes: 31 additions & 13 deletions test/cloud-slack-dev-e2e/botkube_page_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (p *BotkubeCloudPage) SetupSlackWorkspace(t *testing.T, channel string) {

// filter by channel, to make sure that it's visible on the first table page, in order to select it in the next step
t.Log("Filtering by channel name")
p.page.Mouse.MustScroll(10, 5000) // scroll bottom, as the footer collides with selecting filter
p.page.Mouse.MustScroll(10, 5000) // scroll bottom, as the footer collides with selecting filter
p.page.Screenshot()
p.page.MustElement("table th:nth-child(3) span.ant-dropdown-trigger.ant-table-filter-trigger").MustClick()

Expand All @@ -172,40 +172,58 @@ func (p *BotkubeCloudPage) SetupSlackWorkspace(t *testing.T, channel string) {

func (p *BotkubeCloudPage) FinishWizard(t *testing.T) {
t.Log("Navigating to plugin selection")
p.page.Screenshot()

p.page.MustElementR("button", "/^Next$/i").MustClick()
p.page.Screenshot()
p.page.Screenshot("before-first-next")

p.page.MustElementR("button", "/^Next$/i").
MustWaitEnabled().
// we need to wait, otherwise, we click the same 'Next' button twice, before the query is executed, and we are really
// moved to the next step. If we have the navigation updated for each step, it will be resolved.
MustClick().MustWaitStable()

p.page.Screenshot("after-first-next")

t.Log("Using pre-selected plugins. Navigating to wizard summary")
p.page.MustElementR("button", "/^Next$/i").MustClick()
p.page.Screenshot()
p.page.MustElementR("button", "/^Next$/i").
MustWaitEnabled().
// we need to wait, otherwise, we click the same 'Next' button twice, before the query is executed, and we are really
// moved to the next step. If we have the navigation updated for each step, it will be resolved.
MustClick().MustWaitStable()
p.page.Screenshot("after-second-next")

t.Log("Submitting changes")
p.page.MustElementR("button", "/^Deploy changes$/i").MustClick()
p.page.MustElementR("button", "/^Deploy changes$/i").
MustWaitEnabled().
MustClick()
p.page.Screenshot("after-deploy-changes")

// wait till gql mutation passes, and navigates to install details, otherwise, we could navigate to instance details with state 'draft'
p.page.MustWaitNavigation()
p.page.Screenshot()
}

func (p *BotkubeCloudPage) UpdateKubectlNamespace(t *testing.T) {
t.Log("Updating 'kubectl' namespace property")
p.page.MustElementR(`div[role="tab"]`, "Plugins").MustClick()
p.page.MustElementR(`div[role="tab"]`, "Plugins").MustFocus().MustClick().MustWaitStable()

p.page.MustWaitStable()
p.page.MustElement(`button[id^="botkube/kubectl_"]`).MustClick()
p.page.MustElement(`div[data-node-key="ui-form"]`).MustClick()
p.page.MustElementR("input#root_defaultNamespace", "default").MustSelectAllText().MustInput("kube-system")
p.page.MustElementR("button", "/^Update$/i").MustClick()

t.Log("Submitting changes")
p.page.MustElementR("button", "/^Deploy changes$/i").MustClick().MustWaitStable() // use the case-insensitive flag "i"
p.page.MustElementR("button", "/^Deploy changes$/i").MustClick().MustWaitStable()
}

func (p *BotkubeCloudPage) VerifyUpdatedKubectlNamespace(t *testing.T) {
t.Log("Verifying that the 'namespace' value was updated and persisted properly")

p.page.Screenshot()
p.page.MustElementR(`div[role="tab"]`, "Plugins").MustClick()
p.page.Screenshot("before-selecting-plugins-tab")
p.page.MustElementR(`div[role="tab"]`, "Plugins").MustFocus().MustClick().MustWaitStable()
p.page.Screenshot("after-selecting-plugins-tab")
p.page.MustElement(`button[id^="botkube/kubectl_"]`).MustClick()
p.page.MustElement(`div[data-node-key="ui-form"]`).MustClick()
p.page.Screenshot()
p.page.Screenshot("after-selecting-kubectl-cfg-form")
p.page.MustElementR("input#root_defaultNamespace", "kube-system")
}

Expand Down
54 changes: 28 additions & 26 deletions test/cloud-slack-dev-e2e/cloud_slack_dev_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func TestCloudSlackE2E(t *testing.T) {
require.NoError(t, err)

cfg.Slack.Tester.CloudBasedTestEnabled = false // override property used only in the Cloud Slack E2E tests
cfg.Slack.Tester.RecentMessagesLimit = 4 // this is used effectively only for the Botkube restarts. There are two of them in a short time window, so it shouldn't be higher than 5.
cfg.Slack.Tester.RecentMessagesLimit = 3 // this is used effectively only for the Botkube restarts. There are two of them in a short time window, so it shouldn't be higher than 5.

var botkubeDeploymentUninstalled atomic.Bool
botkubeDeploymentUninstalled.Store(true) // not yet installed
Expand Down Expand Up @@ -436,40 +436,40 @@ func removeSourcesAndAddActions(t *testing.T, gql *graphql.Client, existingDeplo
var updateInput struct {
UpdateDeployment gqlModel.Deployment `graphql:"updateDeployment(id: $id, input: $input)"`
}

var updatePluginGroup []*gqlModel.PluginConfigurationGroupUpdateInput
for _, createdPlugin := range existingDeployment.Plugins {
var pluginConfigs []*gqlModel.PluginConfigurationUpdateInput
pluginConfig := gqlModel.PluginConfigurationUpdateInput{
Name: createdPlugin.ConfigurationName,
Configuration: createdPlugin.Configuration,
}
pluginConfigs = append(pluginConfigs, &pluginConfig)
plugin := gqlModel.PluginConfigurationGroupUpdateInput{
ID: &createdPlugin.ID,
Name: createdPlugin.Name,
Type: createdPlugin.Type,
DisplayName: createdPlugin.DisplayName,
Configurations: pluginConfigs,
}
updatePluginGroup = append(updatePluginGroup, &plugin)
}
var updatePlugins []*gqlModel.PluginsUpdateInput
updatePlugin := gqlModel.PluginsUpdateInput{
Groups: updatePluginGroup,
updatePluginGroup = append(updatePluginGroup, &gqlModel.PluginConfigurationGroupUpdateInput{
ID: &createdPlugin.ID,
Name: createdPlugin.Name,
Type: createdPlugin.Type,
DisplayName: createdPlugin.DisplayName,
Configurations: []*gqlModel.PluginConfigurationUpdateInput{
{
Name: createdPlugin.ConfigurationName,
Configuration: createdPlugin.Configuration,
},
},
})
}
updatePlugins = append(updatePlugins, &updatePlugin)

platforms := gqlModel.PlatformsUpdateInput{}

for _, slack := range existingDeployment.Platforms.CloudSlacks {
var channelUpdateInputs []*gqlModel.ChannelBindingsByNameAndIDUpdateInput
for _, channel := range slack.Channels {
var executors []*string
for _, e := range channel.Bindings.Executors {
if strings.Contains(strings.ToLower(e), "ai") { // AI is also a source plugin
continue
}
executors = append(executors, &e)
}
channelUpdateInputs = append(channelUpdateInputs, &gqlModel.ChannelBindingsByNameAndIDUpdateInput{
ChannelID: "", // this is used for UI only so we don't need to provide it
Name: channel.Name,
Bindings: &gqlModel.BotBindingsUpdateInput{
Sources: nil,
Executors: []*string{&channel.Bindings.Executors[0]},
Sources: []*string{},
Executors: executors,
},
})
}
Expand All @@ -486,9 +486,11 @@ func removeSourcesAndAddActions(t *testing.T, gql *graphql.Client, existingDeplo
"input": gqlModel.DeploymentUpdateInput{
Name: existingDeployment.Name,
ResourceVersion: existingDeployment.ResourceVersion,
Plugins: updatePlugins,
Platforms: &platforms,
Actions: CreateActionUpdateInput(existingDeployment),
Plugins: []*gqlModel.PluginsUpdateInput{
{Groups: updatePluginGroup},
},
Platforms: &platforms,
Actions: CreateActionUpdateInput(existingDeployment),
},
}
err := gql.Mutate(context.Background(), &updateInput, updateVariables)
Expand Down
15 changes: 10 additions & 5 deletions test/cloud-slack-dev-e2e/page_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const (
// Currently, we get:
// This browser won’t be supported starting September 1st, 2024. Update your browser to keep using Slack. Learn more:
// https://slack.com/intl/en-gb/help/articles/1500001836081-Slack-support-life-cycle-for-operating-systems-app-versions-and-browsers
chromeUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
chromeUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
)

type Page struct {
Expand All @@ -32,7 +32,7 @@ type Page struct {
cfg E2ESlackConfig
}

func (p Page) Screenshot() {
func (p Page) Screenshot(suffix ...string) {
p.t.Helper()
if p.cfg.ScreenshotsDir == "" {
return
Expand All @@ -41,7 +41,7 @@ func (p Page) Screenshot() {
pathParts := strings.Split(p.cfg.ScreenshotsDir, "/")
pathParts = append(pathParts)

filePath := filepath.Join(p.cfg.ScreenshotsDir, fmt.Sprintf("%d.png", time.Now().UnixNano()))
filePath := filepath.Join(p.cfg.ScreenshotsDir, fmt.Sprintf("%d%s.png", time.Now().UnixNano(), screenshotSuffix(suffix)))

logMsg := fmt.Sprintf("Saving screenshot to %q", filePath)
if p.cfg.DebugMode {
Expand All @@ -63,6 +63,13 @@ func (p Page) Screenshot() {
assert.NoError(p.t, err)
}

func screenshotSuffix(suffix []string) string {
if len(suffix) == 0 {
return ""
}
return "-" + strings.Join(suffix, "-")
}

func closePage(t *testing.T, name string, page *rod.Page) {
t.Helper()
err := page.Close()
Expand All @@ -75,7 +82,6 @@ func closePage(t *testing.T, name string, page *rod.Page) {
}
}


func newBrowserPage(t *testing.T, browser *rod.Browser, cfg E2ESlackConfig) *rod.Page {
t.Helper()

Expand All @@ -88,4 +94,3 @@ func newBrowserPage(t *testing.T, browser *rod.Browser, cfg E2ESlackConfig) *rod
page.MustSetViewport(1200, 1080, 1, false)
return page
}

0 comments on commit 0fa00e0

Please sign in to comment.