tests(e2e): Allow tests to run only on file changes
- supports glob patterns in testfiles - only runs tests on changes - always runs tests without specified patterns tests(e2e): refactor global watch patterns tests(e2e): add watch patterns to test files
This commit is contained in:
parent
f2a23c962a
commit
7765153b40
21 changed files with 260 additions and 7 deletions
|
@ -1,4 +1,16 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/repo/actions/**
|
||||||
|
// web_src/css/actions.css
|
||||||
|
// web_src/js/components/ActionRunStatus.vue
|
||||||
|
// web_src/js/components/RepoActionView.vue
|
||||||
|
// modules/actions/**
|
||||||
|
// modules/structs/workflow.go
|
||||||
|
// routers/api/v1/repo/action.go
|
||||||
|
// routers/web/repo/actions/**
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
114
tests/e2e/changes.go
Normal file
114
tests/e2e/changes.go
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
changesetFiles []string
|
||||||
|
changesetAvailable bool
|
||||||
|
globalFullRun bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func initChangedFiles() {
|
||||||
|
var changes string
|
||||||
|
changes, changesetAvailable = os.LookupEnv("CHANGED_FILES")
|
||||||
|
// the output of the Action seems to actually contain \n and not a newline literal
|
||||||
|
changesetFiles = strings.Split(changes, `\n`)
|
||||||
|
log.Info("Only running tests covered by a subset of test files. Received the following list of CHANGED_FILES: %q", changesetFiles)
|
||||||
|
|
||||||
|
globalPatterns := []string{
|
||||||
|
// meta and config
|
||||||
|
"Makefile",
|
||||||
|
"playwright.config.js",
|
||||||
|
".forgejo/workflows/testing.yml",
|
||||||
|
"tests/e2e/*.go",
|
||||||
|
"tests/e2e/shared/*",
|
||||||
|
// frontend files
|
||||||
|
"frontend/*.js",
|
||||||
|
"frontend/{base,index}.css",
|
||||||
|
// templates
|
||||||
|
"templates/base/**",
|
||||||
|
}
|
||||||
|
fullRunPatterns := []glob.Glob{}
|
||||||
|
for _, expr := range globalPatterns {
|
||||||
|
fullRunPatterns = append(fullRunPatterns, glob.MustCompile(expr, '.', '/'))
|
||||||
|
}
|
||||||
|
globalFullRun = false
|
||||||
|
for _, changedFile := range changesetFiles {
|
||||||
|
for _, pattern := range fullRunPatterns {
|
||||||
|
if pattern.Match(changedFile) {
|
||||||
|
globalFullRun = true
|
||||||
|
log.Info("Changed files match global test pattern, running all tests")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func canSkipTest(testFile string) bool {
|
||||||
|
// run all tests when environment variable is not set or changes match global pattern
|
||||||
|
if !changesetAvailable || globalFullRun {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, changedFile := range changesetFiles {
|
||||||
|
if strings.HasSuffix(testFile, changedFile) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, pattern := range getWatchPatterns(testFile) {
|
||||||
|
if pattern.Match(changedFile) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWatchPatterns(filename string) []glob.Glob {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
watchSection := false
|
||||||
|
patterns := []glob.Glob{}
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
// check for watch block
|
||||||
|
if strings.HasPrefix(line, "// @watch") {
|
||||||
|
if watchSection {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
watchSection = true
|
||||||
|
}
|
||||||
|
if !watchSection {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
line = strings.TrimPrefix(line, "// ")
|
||||||
|
if line != "" {
|
||||||
|
globPattern, err := glob.Compile(line, '.', '/')
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Invalid glob pattern '%s' (skipped): %v", line, err)
|
||||||
|
}
|
||||||
|
patterns = append(patterns, globPattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if no watch block in file
|
||||||
|
if !watchSection {
|
||||||
|
patterns = append(patterns, glob.MustCompile("*"))
|
||||||
|
}
|
||||||
|
return patterns
|
||||||
|
}
|
|
@ -1,4 +1,9 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// web_src/js/components/DashboardRepoList.vue
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ func TestMain(m *testing.M) {
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
tests.InitTest(true)
|
tests.InitTest(true)
|
||||||
|
initChangedFiles()
|
||||||
testE2eWebRoutes = routers.NormalRoutes()
|
testE2eWebRoutes = routers.NormalRoutes()
|
||||||
|
|
||||||
os.Unsetenv("GIT_AUTHOR_NAME")
|
os.Unsetenv("GIT_AUTHOR_NAME")
|
||||||
|
@ -100,6 +101,11 @@ func TestE2e(t *testing.T) {
|
||||||
_, filename := filepath.Split(path)
|
_, filename := filepath.Split(path)
|
||||||
testname := filename[:len(filename)-len(filepath.Ext(path))]
|
testname := filename[:len(filename)-len(filepath.Ext(path))]
|
||||||
|
|
||||||
|
if canSkipTest(path) {
|
||||||
|
fmt.Printf("No related changes for test, skipping: %s\n", filename)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
t.Run(testname, func(t *testing.T) {
|
t.Run(testname, func(t *testing.T) {
|
||||||
// Default 2 minute timeout
|
// Default 2 minute timeout
|
||||||
onForgejoRun(t, func(*testing.T, *url.URL) {
|
onForgejoRun(t, func(*testing.T, *url.URL) {
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/user/auth/**
|
||||||
|
// web_src/js/features/user-**
|
||||||
|
// modules/{user,auth}/**
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, save_visual} from './utils_e2e.js';
|
import {test, login_user, save_visual} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
// document is a global in evaluate, so it's safe to ignore here
|
// document is a global in evaluate, so it's safe to ignore here
|
||||||
// eslint playwright/no-conditional-in-test: 0
|
// eslint playwright/no-conditional-in-test: 0
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/explore/**
|
||||||
|
// web_src/modules/fomantic/**
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test} from './utils_e2e.js';
|
import {test} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// web_src/js/features/comp/**
|
||||||
|
// web_src/js/features/repo-**
|
||||||
|
// templates/repo/issue/view_content/*
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, login} from './utils_e2e.js';
|
import {test, login_user, login} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/repo/issue/view_content/**
|
||||||
|
// web_src/css/repo/issue-**
|
||||||
|
// web_src/js/features/repo-issue**
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, login} from './utils_e2e.js';
|
import {test, login_user, login} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// web_src/js/features/comp/ComboMarkdownEditor.js
|
||||||
|
// web_src/css/editor/combomarkdowneditor.css
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, load_logged_in_context, login_user} from './utils_e2e.js';
|
import {test, load_logged_in_context, login_user} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// web_src/css/markup/**
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test} from './utils_e2e.js';
|
import {test} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/org/team/new.tmpl
|
||||||
|
// web_src/css/form.css
|
||||||
|
// web_src/js/features/org-team.js
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, login} from './utils_e2e.js';
|
import {test, login_user, login} from './utils_e2e.js';
|
||||||
import {validate_form} from './shared/forms.js';
|
import {validate_form} from './shared/forms.js';
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// routers/web/user/**
|
||||||
|
// templates/shared/user/**
|
||||||
|
// web_src/js/features/common-global.js
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// web_src/js/features/comp/ReactionSelector.js
|
||||||
|
// routers/web/repo/issue.go
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// models/repo/attachment.go
|
||||||
|
// modules/structs/attachment.go
|
||||||
|
// routers/web/repo/**
|
||||||
|
// services/attachment/**
|
||||||
|
// services/release/**
|
||||||
|
// templates/repo/release/**
|
||||||
|
// web_src/js/features/repo-release.js
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, save_visual, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, save_visual, load_logged_in_context} from './utils_e2e.js';
|
||||||
import {validate_form} from './shared/forms.js';
|
import {validate_form} from './shared/forms.js';
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// web_src/js/features/repo-code.js
|
||||||
|
// web_src/css/repo.css
|
||||||
|
// services/gitdiff/**
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
||||||
|
|
||||||
|
@ -77,10 +84,3 @@ test('Readable diff', async ({page}, workerInfo) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Commit graph overflow', async ({page}) => {
|
|
||||||
await page.goto('/user2/diff-test/graph');
|
|
||||||
await expect(page.getByRole('button', {name: 'Mono'})).toBeInViewport({ratio: 1});
|
|
||||||
await expect(page.getByRole('button', {name: 'Color'})).toBeInViewport({ratio: 1});
|
|
||||||
await expect(page.locator('.selection.search.dropdown')).toBeInViewport({ratio: 1});
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/repo/graph.tmpl
|
||||||
|
// web_src/css/features/gitgraph.css
|
||||||
|
// web_src/js/features/repo-graph.js
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
||||||
|
|
||||||
|
@ -6,6 +13,13 @@ test.beforeAll(async ({browser}, workerInfo) => {
|
||||||
await login_user(browser, workerInfo, 'user2');
|
await login_user(browser, workerInfo, 'user2');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Commit graph overflow', async ({page}) => {
|
||||||
|
await page.goto('/user2/diff-test/graph');
|
||||||
|
await expect(page.getByRole('button', {name: 'Mono'})).toBeInViewport({ratio: 1});
|
||||||
|
await expect(page.getByRole('button', {name: 'Color'})).toBeInViewport({ratio: 1});
|
||||||
|
await expect(page.locator('.selection.search.dropdown')).toBeInViewport({ratio: 1});
|
||||||
|
});
|
||||||
|
|
||||||
test('Switch branch', async ({browser}, workerInfo) => {
|
test('Switch branch', async ({browser}, workerInfo) => {
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
|
@ -1,4 +1,9 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// web_src/js/features/repo-migrate.js
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/webhook/shared-settings.tmpl
|
||||||
|
// templates/repo/settings/**
|
||||||
|
// web_src/css/{form,repo}.css
|
||||||
|
// web_src/css/modules/grid.css
|
||||||
|
// web_src/js/features/comp/WebHookEditor.js
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, login} from './utils_e2e.js';
|
import {test, login_user, login} from './utils_e2e.js';
|
||||||
import {validate_form} from './shared/forms.js';
|
import {validate_form} from './shared/forms.js';
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/repo/wiki/**
|
||||||
|
// web_src/css/repo**
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test} from './utils_e2e.js';
|
import {test} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/org/**
|
||||||
|
// templates/repo/**
|
||||||
|
// web_src/js/webcomponents/overflow-menu.js
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// @watch start
|
||||||
|
// templates/user/auth/**
|
||||||
|
// templates/user/settings/**
|
||||||
|
// web_src/js/features/user-**
|
||||||
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue