migrate integration tests to bdd (#1497)

Migrates the existing e2e tests towards a cucumber bdd-style and utilizes the integration-test-runner package.

Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
Konstantin Schaper
2021-01-26 12:58:48 +01:00
committed by GitHub
parent d737c3aa9f
commit 94304f96a3
24 changed files with 1763 additions and 547 deletions

View File

@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add Font ttf-dejavu to oci image ([#1498](https://github.com/scm-manager/scm-manager/issues/1498))
### Changed
- Migrate integration tests to bdd ([#1497](https://github.com/scm-manager/scm-manager/pull/1497))
- Layout of proxy settings ([#1502](https://github.com/scm-manager/scm-manager/pull/1502))
- Apply test ids to production builds for usage in e2e tests ([#1499](https://github.com/scm-manager/scm-manager/pull/1499))
- Bump google guava version to 30.1-jre

View File

@@ -34,6 +34,7 @@ import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.Priorities;
import sonia.scm.SCMContext;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.AnonymousMode;
@@ -49,6 +50,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Set;
/**
@@ -69,7 +71,7 @@ public class AuthenticationFilter extends HttpFilter {
private static final String ATTRIBUTE_FAILED_AUTH = "sonia.scm.auth.failed";
private final Set<WebTokenGenerator> tokenGenerators;
private final List<WebTokenGenerator> tokenGenerators;
protected ScmConfiguration configuration;
/**
@@ -81,7 +83,7 @@ public class AuthenticationFilter extends HttpFilter {
@Inject
public AuthenticationFilter(ScmConfiguration configuration, Set<WebTokenGenerator> tokenGenerators) {
this.configuration = configuration;
this.tokenGenerators = tokenGenerators;
this.tokenGenerators = Priorities.sortInstances(tokenGenerators);
}
/**

View File

@@ -68,6 +68,7 @@ public class TestData {
public static void cleanup() {
LOG.info("start to clean up to integration tests");
cleanupConfig();
cleanupRepositories();
cleanupGroups();
cleanupUsers();
@@ -231,6 +232,40 @@ public class TestData {
users.stream().filter(url -> PROTECTED_USERS.stream().noneMatch(url::contains)).forEach(TestData::delete);
}
private static void cleanupConfig() {
given(VndMediaType.CONFIG).accept("application/json")
.when()
.body("{\n" +
" \"proxyPassword\": null,\n" +
" \"proxyPort\": 8080," +
" \"proxyServer\": \"proxy.mydomain.com\",\n" +
" \"proxyUser\": null,\n" +
" \"enableProxy\": false,\n" +
" \"realmDescription\": \"SONIA :: SCM Manager\",\n" +
" \"disableGroupingGrid\": false,\n" +
" \"dateFormat\": \"YYYY-MM-DD HH:mm:ss\",\n" +
" \"anonymousAccessEnabled\": false,\n" +
" \"anonymousMode\": \"OFF\",\n" +
" \"baseUrl\": \"http://localhost:8081/scm\",\n" +
" \"forceBaseUrl\": false,\n" +
" \"loginAttemptLimit\": -1,\n" +
" \"proxyExcludes\": [],\n" +
" \"skipFailedAuthenticators\": false,\n" +
" \"pluginUrl\": \"https://plugin-center-api.scm-manager.org/api/v1/plugins/{version}?os={os}&arch={arch}&jre={jre}\", \n" +
" \"loginAttemptLimitTimeout\": 300, \n" +
" \"enabledXsrfProtection\": true, \n" +
" \"enabledUserConverter\": false, \n" +
" \"namespaceStrategy\": \"UsernameNamespaceStrategy\", \n" +
" \"loginInfoUrl\": \"https://login-info.scm-manager.org/api/v1/login-info\",\n" +
" \"releaseFeedUrl\": \"https://scm-manager.org/download/rss.xml\",\n" +
" \"mailDomainName\": \"scm-manager.local\"\n" +
"}")
.put(createResourceUrl("config"))
.then()
.statusCode(HttpStatus.SC_NO_CONTENT);
LOG.info("wrote default configuration");
}
private static void delete(String url) {
given(VndMediaType.REPOSITORY)
.when()

View File

@@ -0,0 +1,73 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.it.webapp;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import org.junit.Assert;
import sonia.scm.api.rest.ObjectMapperProvider;
import sonia.scm.api.v2.resources.ConfigDto;
import sonia.scm.web.VndMediaType;
import java.io.IOException;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static sonia.scm.it.webapp.IntegrationTestUtil.createResource;
public class ConfigUtil {
public static ConfigDto readConfig(ScmClient client) {
WebResource.Builder wr = createResource(client, "config");
ClientResponse response = wr.get(ClientResponse.class);
assertNotNull(response);
Assert.assertEquals(200, response.getStatus());
String json = response.getEntity(String.class);
ConfigDto config = null;
try {
config = new ObjectMapperProvider().get().readerFor(ConfigDto.class).readValue(json);
} catch (IOException e) {
fail("could not read json:\n" + json);
}
response.close();
assertNotNull(config);
return config;
}
public static void writeConfig(ScmClient client, ConfigDto config) {
ClientResponse response =
createResource(client, "config")
.accept("*/*")
.type(VndMediaType.CONFIG)
.put(ClientResponse.class, config);
Assert.assertEquals(204, response.getStatus());
response.close();
}
}

View File

@@ -30,15 +30,16 @@ import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import org.junit.Assert;
import sonia.scm.api.rest.ObjectMapperProvider;
import sonia.scm.api.v2.resources.ConfigDto;
import sonia.scm.api.v2.resources.RepositoryDto;
import sonia.scm.web.VndMediaType;
import java.io.IOException;
import java.net.URI;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static sonia.scm.it.webapp.ConfigUtil.readConfig;
import static sonia.scm.it.webapp.IntegrationTestUtil.BASE_URL;
import static sonia.scm.it.webapp.IntegrationTestUtil.createResource;
import static sonia.scm.it.webapp.IntegrationTestUtil.getLink;
@@ -50,7 +51,17 @@ public final class RepositoryITUtil
private RepositoryITUtil() {}
public static void setNamespaceStrategy(ScmClient client, String namespaceStrategy) {
ConfigDto config = readConfig(client);
config.setNamespaceStrategy(namespaceStrategy);
ConfigUtil.writeConfig(client, config);
}
public static RepositoryDto createRepository(ScmClient client, String repositoryJson) {
setNamespaceStrategy(client, "UsernameNamespaceStrategy");
ClientResponse response =
createResource(client, "repositories")
.accept("*/*")

View File

@@ -1,5 +1 @@
{
"baseUrl": "http://localhost:8081/scm",
"videoUploadOnPasses": false,
"videoCompression": false
}
{}

View File

@@ -1,86 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
describe("With Anonymous mode fully enabled", () => {
before("Set anonymous mode to full", () => {
cy.login("scmadmin", "scmadmin");
cy.setAnonymousMode("FULL");
// Give anonymous user permissions
cy.byTestId("primary-navigation-users").click();
cy.byTestId("_anonymous").click();
cy.byTestId("user-settings-link").click();
cy.byTestId("user-permissions-link").click();
cy.byTestId("read-all-repositories").click();
cy.byTestId("set-permissions-button").click();
cy.byTestId("primary-navigation-logout").click();
});
it("Should show repositories overview with Login button in primary navigation", () => {
cy.visit("/repos/");
cy.byTestId("repository-overview-filter");
cy.byTestId("scm-anonymous");
cy.byTestId("primary-navigation-login");
});
it("Should show login page on url", () => {
cy.visit("/login/");
cy.byTestId("login-button");
});
it("Should show login page on link click", () => {
cy.visit("/repos/");
cy.byTestId("repository-overview-filter");
cy.byTestId("primary-navigation-login").click();
cy.byTestId("login-button");
});
it("Should login and direct to repositories overview", () => {
cy.login("scmadmin", "scmadmin");
cy.visit("/login");
cy.byTestId("scm-administrator");
cy.byTestId("primary-navigation-logout").click();
});
it("Should logout and direct to login page", () => {
cy.login("scmadmin", "scmadmin");
cy.visit("/repos/");
cy.byTestId("repository-overview-filter");
cy.byTestId("scm-administrator");
cy.byTestId("primary-navigation-logout").click();
cy.byTestId("login-button");
});
it("Anonymous user should not be able to change password", () => {
cy.visit("/repos/");
cy.byTestId("footer-user-profile").click();
cy.byTestId("scm-anonymous");
cy.containsNotByTestId("ul", "user-settings-link");
cy.get("section").not("Change password");
});
after("Disable anonymous access", () => {
cy.login("scmadmin", "scmadmin");
cy.setAnonymousMode("OFF");
cy.byTestId("primary-navigation-logout").click();
});
});

View File

@@ -0,0 +1,14 @@
Feature: Anonymous Mode Disabled
Background:
Given Anonymous Mode is disabled
Scenario: There is no primary navigation
Given User is not authenticated
When User visits any page
Then There is no primary navigation
Scenario: Authenticated users have a footer navigation
Given User is authenticated
When User visits any page
Then There is a footer navigation

View File

@@ -0,0 +1,27 @@
Feature: Anonymous Mode Full
Background:
Given Full Anonymous Mode is enabled
And User is not authenticated
Scenario: Show login button on anonymous route
Given Anonymous user has permission to read all repositories
When User visits the repository overview page
Then The repository overview page is shown
And There is a login button
Scenario: Show login page
When User visits login page
Then The login page is shown
Scenario: Navigate to login page
When Users clicks login button
Then The login page is shown
Scenario: Redirect to repositories overview after login
When User logs in
Then User should be authenticated
Scenario: Anonymous user cannot change password
When User visits their user settings
Then There is no option to change the password

View File

@@ -0,0 +1,8 @@
Feature: Anonymous Mode with Protocol Only
Background:
Given Protocol Only Anonymous Mode is enabled
Scenario: There is no primary navigation
Given User is not authenticated
When User visits the repository overview page
Then The login page is shown

View File

@@ -21,24 +21,5 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
module.exports = require("@scm-manager/integration-test-runner/plugins");

View File

@@ -1,69 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
const login = (username, password) => {
cy.visit("/login");
cy.byTestId("username-input").type(username);
cy.byTestId("password-input").type(password);
cy.byTestId("login-button").click();
};
const setAnonymousMode = anonymousMode => {
cy.byTestId("primary-navigation-admin").click();
cy.byTestId("admin-settings-link").click();
cy.byTestId("anonymous-mode-select")
.select(anonymousMode)
.should("have.value", anonymousMode);
cy.byTestId("submit-button").click();
};
Cypress.Commands.add("login", login);
Cypress.Commands.add("setAnonymousMode", setAnonymousMode);
Cypress.Commands.add("byTestId", testId => cy.get(`[data-testid=${testId}]`));
Cypress.Commands.add("containsNotByTestId", (container, testId) => cy.get(container).not(`[data-testid=${testId}]`));

View File

@@ -21,24 +21,3 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
describe("With Anonymous mode protocol only enabled", () => {
before("Set anonymous mode to protocol only", () => {
cy.login("scmadmin", "scmadmin");
cy.setAnonymousMode("PROTOCOL_ONLY");
cy.byTestId("primary-navigation-logout").click();
});
it("Should show login page without primary navigation", () => {
cy.visit("/repos/");
cy.byTestId("login-button");
cy.containsNotByTestId("div", "primary-navigation-login");
cy.containsNotByTestId("div", "primary-navigation-repositories");
});
after("Disable anonymous access", () => {
cy.login("scmadmin", "scmadmin");
cy.setAnonymousMode("OFF");
cy.byTestId("primary-navigation-logout").click();
});
});

View File

@@ -21,23 +21,5 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
import "@scm-manager/integration-test-runner/commands";
import "./commands";

View File

@@ -22,23 +22,4 @@
* SOFTWARE.
*/
describe("With Anonymous mode disabled", () => {
before("Disable anonymous access", () => {
cy.login("scmadmin", "scmadmin");
cy.setAnonymousMode("OFF");
cy.byTestId("primary-navigation-logout").click();
});
it("Should show login page without primary navigation", () => {
cy.byTestId("login-button");
cy.containsNotByTestId("div", "primary-navigation-login");
cy.containsNotByTestId("div", "primary-navigation-repositories");
});
it("Should redirect after login", () => {
cy.login("scmadmin", "scmadmin");
cy.visit("/me");
cy.byTestId("footer-user-profile");
cy.byTestId("primary-navigation-logout").click();
});
});
export * from "@scm-manager/integration-test-runner/steps";

View File

@@ -8,25 +8,16 @@
"private": false,
"scripts": {
"headless": "cypress run",
"ci": "node src/index.js"
"ci": "integration-test-runner run -u scmadmin -p scmadmin -d ./"
},
"dependencies": {
"@ffmpeg-installer/ffmpeg": "^1.0.20",
"cypress": "^4.12.0",
"@scm-manager/integration-test-runner": "^1.3.0",
"fluent-ffmpeg": "^2.1.2"
},
"devDependencies": {
"eslint-plugin-cypress": "^2.11.1"
},
"prettier": "@scm-manager/prettier-config",
"eslintConfig": {
"extends": "@scm-manager/eslint-config",
"plugins": [
"cypress"
],
"env": {
"cypress/globals": true
}
"extends": "@scm-manager/eslint-config"
},
"publishConfig": {
"access": "public"

View File

@@ -1,86 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
const cypress = require("cypress");
const fs = require("fs");
const path = require("path");
const ffmpegPath = require("@ffmpeg-installer/ffmpeg").path;
const ffmpeg = require("fluent-ffmpeg");
ffmpeg.setFfmpegPath(ffmpegPath);
const options = {
reporter: "junit",
reporterOptions: {
mochaFile: path.join("..", "build", "reports", "e2e", "TEST-[hash].xml")
}
};
const createOutputFile = test => {
const title = test.title.join(" -- ");
return path.join("cypress", "videos", `${title}.mp4`);
};
const cutVideo = (video, test) => {
return new Promise((resolve, reject) => {
const title = createOutputFile(test);
ffmpeg(video)
.setStartTime(test.videoTimestamp / 1000)
.setDuration(test.wallClockDuration / 1000)
.output(title)
.on("end", err => {
if (err) {
reject(err);
} else {
resolve();
}
})
.on("error", err => {
reject(err);
})
.run();
});
};
cypress
.run(options)
.then(results => {
results.runs.forEach(run => {
// remove videos of successful runs
if (!run.shouldUploadVideo) {
fs.unlinkSync(run.video);
} else {
const cuts = [];
run.tests.forEach(test => {
if (test.state !== "passed") {
cuts.push(cutVideo(run.video, test));
}
});
Promise.all(cuts)
.then(() => fs.unlinkSync(run.video))
.catch(err => console.error("failed to cut video", err));
}
});
})
.catch(err => console.error(err));

View File

@@ -19,7 +19,7 @@
},
"devDependencies": {
"@scm-manager/babel-preset": "^2.10.1",
"@scm-manager/eslint-config": "^2.10.1",
"@scm-manager/eslint-config": "^2.12.0",
"@scm-manager/jest-preset": "^2.12.4",
"@scm-manager/prettier-config": "^2.10.1",
"@scm-manager/tsconfig": "^2.10.1",

View File

@@ -14,7 +14,7 @@
},
"devDependencies": {
"@scm-manager/babel-preset": "^2.10.1",
"@scm-manager/eslint-config": "^2.10.1",
"@scm-manager/eslint-config": "^2.12.0",
"@scm-manager/jest-preset": "^2.12.4",
"@scm-manager/prettier-config": "^2.10.1",
"@scm-manager/tsconfig": "^2.10.1",

View File

@@ -19,7 +19,7 @@
},
"devDependencies": {
"@scm-manager/babel-preset": "^2.10.1",
"@scm-manager/eslint-config": "^2.10.1",
"@scm-manager/eslint-config": "^2.12.0",
"@scm-manager/jest-preset": "^2.12.4",
"@scm-manager/prettier-config": "^2.10.1",
"@scm-manager/tsconfig": "^2.10.1",

View File

@@ -28,7 +28,7 @@
"worker-plugin": "^3.2.0"
},
"devDependencies": {
"@scm-manager/eslint-config": "^2.10.1",
"@scm-manager/eslint-config": "^2.12.0",
"@scm-manager/prettier-config": "^2.10.1"
},
"eslintConfig": {

View File

@@ -16,7 +16,7 @@
"react-diff-view": "^2.4.1"
},
"devDependencies": {
"@scm-manager/eslint-config": "^2.10.1",
"@scm-manager/eslint-config": "^2.12.0",
"@scm-manager/prettier-config": "^2.10.1",
"css-loader": "^3.2.0",
"prettier": "^2.1.2",

View File

@@ -38,6 +38,7 @@ import org.apache.shiro.codec.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.Priority;
import sonia.scm.plugin.Extension;
import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util;
@@ -53,6 +54,7 @@ import javax.servlet.http.HttpServletRequest;
* @author Sebastian Sdorra
* @since 2.0.0
*/
@Priority(100)
@Extension
public class BasicWebTokenGenerator extends SchemeBasedWebTokenGenerator
{

1774
yarn.lock

File diff suppressed because it is too large Load Diff