Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • olasd/swh-web
  • lunar/swh-web
  • jayeshv/swh-web
  • bchauvet/swh-web
  • ardumont/swh-web
  • anlambert/swh-web
  • vlorentz/swh-web
  • swh/devel/swh-web
  • KShivendu/swh-web
  • pabs/swh-web
  • douardda/swh-web
  • bmeles/swh-web
  • marmoute/swh-web
  • rboyer/swh-web
14 results
Show changes
Commits on Source (239)
Showing
with 232 additions and 776 deletions
# Changes here will be overwritten by Copier
_commit: v0.2.0
_commit: v0.3.3
_src_path: https://gitlab.softwareheritage.org/swh/devel/swh-py-template.git
description: Software Heritage web UI
distribution_name: swh-web
......
......@@ -12,12 +12,12 @@ docs/dev-info.md
*.sqlite3*
.directory
node_modules/
static/*.*
static/js/
static/css/
static/fonts/
static/jssources/
static/img/thirdParty/
swh/web/static/*.*
swh/web/static/js/
swh/web/static/css/
swh/web/static/fonts/
swh/web/static/jssources/
swh/web/static/img/thirdParty/
build/
dist/
package-lock.json
......
......@@ -2,7 +2,7 @@ exclude: "^swh/web/tests/resources/"
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
exclude: >
......@@ -13,23 +13,23 @@ repos:
- id: check-yaml
- repo: https://github.com/python/black
rev: 23.1.0
rev: 25.1.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
rev: 6.0.0
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
rev: 7.1.1
hooks:
- id: flake8
additional_dependencies: [flake8-bugbear==22.9.23]
additional_dependencies: [flake8-bugbear==24.12.12, flake8-pyproject]
- repo: https://github.com/codespell-project/codespell
rev: v2.2.2
rev: v2.4.1
hooks:
- id: codespell
name: Check source code spelling
......@@ -38,10 +38,11 @@ repos:
cypress/integration/directory.spec.js|
yarn.lock|
package.json|
swh/web/browse/tests/data/content_iso-8859-7_encoded
swh/web/browse/tests/data/content_iso-8859-7_encoded|
swh/web/add_forge_now/migrations/swh-afn-urls-canonicalise.txt
)$
args: [-L edn, -L crate]
stages: [commit]
args: [-L edn, -L crate, -L THIRDPARTY, -L thirdparty]
stages: [pre-commit]
- id: codespell
name: Check commit message spelling
stages: [commit-msg]
......@@ -67,13 +68,12 @@ repos:
additional_dependencies: [twine, build]
- id: eslint
name: eslint
entry: node_modules/.bin/eslint -c assets/config/.eslintrc
--ignore-path assets/config/.eslintignore
entry: node_modules/.bin/eslint
language: system
types: [javascript]
- repo: https://github.com/Riverside-Healthcare/djLint
rev: v1.31.1
rev: v1.35.2
hooks:
- id: djlint-django
name: Django templates formatter
......@@ -82,7 +82,7 @@ repos:
- --quiet
- repo: https://github.com/Riverside-Healthcare/djLint
rev: v1.31.1
rev: v1.35.2
hooks:
- id: djlint-django
name: Django templates linter
......
......@@ -6,7 +6,7 @@ In the interest of fostering an open and welcoming environment, we as Software
Heritage contributors and maintainers pledge to making participation in our
project and our community a harassment-free experience for everyone, regardless
of age, body size, disability, ethnicity, sex characteristics, gender identity
and expression, level of experience, education, socio-economic status,
and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
......
recursive-include swh/web/static *
\ No newline at end of file
......@@ -2,130 +2,128 @@ TEST_DIRS := ./swh/web/
TESTFLAGS += --hypothesis-profile=swh-web-fast
TESTFULL_FLAGS = --hypothesis-profile=swh-web
YARN ?= yarn
SETTINGS_TEST ?= swh.web.settings.tests
SETTINGS_CYPRESS ?= swh.web.settings.cypress
SETTINGS_DEV ?= swh.web.settings.development
SETTINGS_PROD = swh.web.settings.production
define run_django_migrations
django-admin migrate --settings=$(1) -v0
endef
define create_django_fixtures
cat swh/web/tests/create_test_admin.py | django-admin shell --settings=$(1)
cat swh/web/tests/create_test_users.py | django-admin shell --settings=$(1)
cat swh/web/tests/create_test_alter.py | django-admin shell --settings=$(1)
endef
define run_django_server
python3 swh/web/manage.py runserver --nostatic --settings=$(1)
endef
yarn-install: package.json
$(YARN) install --frozen-lockfile
.PHONY: build-webpack-dev
build-webpack-dev: yarn-install
build-webpack-dev: yarn-install ## Build frontend assets with webpack
$(YARN) build-dev
.PHONY: build-webpack-test
build-webpack-test: yarn-install
build-webpack-test: yarn-install ## | same with coverage activated
$(YARN) build-test
.PHONY: build-webpack-dev-no-verbose
build-webpack-dev-no-verbose: yarn-install
build-webpack-dev-no-verbose: yarn-install ## | same as above without any output
$(YARN) build-dev >/dev/null
.PHONY: build-webpack-prod
build-webpack-prod: yarn-install
build-webpack-prod: yarn-install ## | build assets minified and with mappings for sentry
$(YARN) build
.PHONY: run-migrations-dev
run-migrations-dev:
python3 swh/web/manage.py rename_app --settings=$(SETTINGS_DEV) swh_web_common swh_web_save_code_now
python3 swh/web/manage.py migrate --settings=$(SETTINGS_DEV) -v0
run-migrations-dev: ## Run django db migration (dev: swh.web.settings.development)
$(call run_django_migrations,$(SETTINGS_DEV))
.PHONY: run-migrations-prod
run-migrations-prod:
django-admin rename_app --settings=$(SETTINGS_PROD) swh_web_common swh_web_save_code_now
django-admin migrate --settings=$(SETTINGS_PROD) -v0
run-migrations-prod: ## | same with prod settings (swh.web.settings.production)
$(call run_django_migrations,$(SETTINGS_PROD))
.PHONY: run-migrations-test
run-migrations-test:
run-migrations-cypress: ## | same with cypress settings (swh.web.cypress.tests)
rm -f swh-web-test*.sqlite3*
django-admin migrate --settings=$(SETTINGS_TEST) -v0
$(call run_django_migrations,$(SETTINGS_CYPRESS))
add-users-test: run-migrations-test
cat swh/web/tests/create_test_admin.py | django-admin shell --settings=$(SETTINGS_TEST)
cat swh/web/tests/create_test_users.py | django-admin shell --settings=$(SETTINGS_TEST)
add-users-cypress: run-migrations-cypress ## Create default django users (cypress settings)
$(call create_django_fixtures,$(SETTINGS_CYPRESS))
add-users-dev: run-migrations-dev
cat swh/web/tests/create_test_admin.py | django-admin shell --settings=$(SETTINGS_DEV)
cat swh/web/tests/create_test_users.py | django-admin shell --settings=$(SETTINGS_DEV)
add-users-dev: run-migrations-dev ## | same, using dev settings
$(call create_django_fixtures,$(SETTINGS_DEV))
add-users-prod: run-migrations-prod
cat swh/web/tests/create_test_admin.py | django-admin shell --settings=$(SETTINGS_PROD)
cat swh/web/tests/create_test_users.py | django-admin shell --settings=$(SETTINGS_PROD)
add-users-prod: run-migrations-prod ## | same, using prod settings
$(call create_django_fixtures,$(SETTINGS_PROD))
.PHONY: clear-memcached
clear-memcached:
clear-memcached: ## Clear locally running memcache (on localhost:1211)
echo "flush_all" | nc -q 2 localhost 11211 2>/dev/null
run-django-webpack-devserver: add-users-dev yarn-install
run-django-webpack-devserver: add-users-dev yarn-install ## Start webpack and django servers using dev settings (frontend and backend parts of the webapp get automatically reloaded when source files are modified)
bash -c "trap 'trap - SIGINT SIGTERM ERR EXIT && \
# ensure all child processes will be killed by PGID when exiting \
ps -o pgid= $$$$ | grep -o [0-9]* | xargs pkill -g' SIGINT SIGTERM ERR EXIT; \
$(YARN) start-dev & sleep 10 && cd swh/web && \
python3 manage.py runserver --nostatic --settings=$(SETTINGS_DEV) || exit 1"
run-django-webpack-dev: build-webpack-dev add-users-dev
python3 swh/web/manage.py runserver --nostatic --settings=$(SETTINGS_DEV)
run-django-webpack-dev: build-webpack-dev add-users-dev ## Build assets & start django from src using dev settings
$(call run_django_server,$(SETTINGS_DEV))
run-django-webpack-prod: build-webpack-prod add-users-prod clear-memcached
python3 swh/web/manage.py runserver --nostatic --settings=$(SETTINGS_PROD)
run-django-webpack-prod: build-webpack-prod add-users-prod clear-memcached ## | same with prod settings
$(call run_django_server,$(SETTINGS_PROD))
run-django-server-dev: add-users-dev
python3 swh/web/manage.py runserver --nostatic --settings=$(SETTINGS_DEV)
run-django-server-dev: add-users-dev ## Start django from src using dev settings
$(call run_django_server,$(SETTINGS_DEV))
run-django-server-prod: add-users-prod clear-memcached
python3 swh/web/manage.py runserver --nostatic --settings=$(SETTINGS_PROD)
run-django-server-prod: add-users-prod clear-memcached ## | same with prod settings
$(call run_django_server,$(SETTINGS_PROD))
run-gunicorn-server: add-users-prod clear-memcached
run-gunicorn-server: add-users-prod clear-memcached ## Clear memcache and start django from gunicorn (prod settings)
DJANGO_SETTINGS_MODULE=$(SETTINGS_PROD) \
gunicorn --bind 127.0.0.1:5004 \
--threads 2 \
--workers 2 'django.core.wsgi:get_wsgi_application()'
run-django-webpack-memory-storages: build-webpack-dev add-users-test
python3 swh/web/manage.py runserver --nostatic --settings=$(SETTINGS_TEST)
run-django-webpack-memory-storages: build-webpack-dev add-users-dev ## Start django from tests settings (using in-memory storages)
$(call run_django_server,$(SETTINGS_CYPRESS))
run-mirror-demo: build-webpack-dev add-users-test
SWH_CONFIG_FILENAME=$$PWD/mirror_demo/config.yml python3 swh/web/manage.py runserver \
--nostatic --settings=$(SETTINGS_TEST)
run-mirror-demo: build-webpack-dev add-users-dev ## Start django from tests config using a mirror setup
SWH_CONFIG_FILENAME=$$PWD/mirror_demo/config.yml $(call run_django_server,$(SETTINGS_DEV))
test-full:
test-full: ## Run all python tests
$(TEST) $(TESTFULL_FLAGS) $(TEST_DIRS)
.PHONY: test-frontend-cmd
test-frontend-cmd: build-webpack-test add-users-test
test-frontend-cmd: build-webpack-test add-users-cypress
bash -c "trap 'trap - SIGINT SIGTERM ERR EXIT && \
jobs -p | xargs -r kill' SIGINT SIGTERM ERR EXIT; \
python3 swh/web/manage.py runserver --nostatic --settings=$(SETTINGS_TEST) & \
$(call run_django_server,$(SETTINGS_CYPRESS)) & \
sleep 10 && $(YARN) run cypress run --config numTestsKeptInMemory=0 && \
$(YARN) mochawesome && $(YARN) nyc-report"
test-frontend: export CYPRESS_SKIP_SLOW_TESTS=1
test-frontend: test-frontend-cmd
test-frontend: test-frontend-cmd ## Run cypress non-slow tests (no GUI)
test-frontend-full: export CYPRESS_SKIP_SLOW_TESTS=0
test-frontend-full: test-frontend-cmd
test-frontend-full: test-frontend-cmd ## | same, including slow tests
.PHONY: test-frontend-ui-cmd
test-frontend-ui-cmd: add-users-test yarn-install
test-frontend-ui-cmd: add-users-cypress yarn-install
# ensure all child processes will be killed when hitting Ctrl-C in terminal
# or manually closing the Cypress UI window, killing by PGID seems the only
# reliable way to do it in that case
bash -c "trap 'trap - SIGINT SIGTERM ERR EXIT && \
ps -o pgid= $$$$ | grep -o [0-9]* | xargs pkill -g' SIGINT SIGTERM ERR EXIT; \
$(YARN) start-dev & \
python3 swh/web/manage.py runserver --nostatic --settings=$(SETTINGS_TEST) & \
$(call run_django_server,$(SETTINGS_CYPRESS)) & \
sleep 10 && $(YARN) run cypress open"
test-frontend-ui: export CYPRESS_SKIP_SLOW_TESTS=1
test-frontend-ui: test-frontend-ui-cmd
test-frontend-ui: test-frontend-ui-cmd ## Run cypress non-slow tests in a browser (GUI)
test-frontend-full-ui: export CYPRESS_SKIP_SLOW_TESTS=0
test-frontend-full-ui: test-frontend-ui-cmd
test-frontend-full-ui: test-frontend-ui-cmd ## | same, including slow tests
.PHONY: help
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sed -e s/Makefile.local:// | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
# Override default rule to make sure DJANGO env var is properly set. It
# *should* work without any override thanks to the mypy django-stubs plugin,
# but it currently doesn't; see
# https://github.com/typeddjango/django-stubs/issues/166
check-mypy:
DJANGO_SETTINGS_MODULE=$(SETTINGS_DEV) $(MYPY) $(MYPYFLAGS) swh
.DEFAULT_GOAL := help
......@@ -78,7 +78,7 @@ Frontend requirements
^^^^^^^^^^^^^^^^^^^^^
To compile the frontend assets, you need to have `nodejs <https://nodejs.org>`_
>= 14.0.0 and `yarn`_ installed. If you are on Debian,
>= 20 and `yarn`_ installed. If you are on Debian,
you can easily install an up to date nodejs from the `nodesource
<https://github.com/nodesource/distributions/blob/master/README.md>`_
repository.
......
# Output debugging info
# loglevel: debug
# Major version of Bootstrap: 3 or 4
bootstrapVersion: 4
# Webpack loaders, order matters
styleLoaders:
- 'css-loader?{"sourceMap": true}'
- 'postcss-loader?{"sourceMap": true}'
- 'sass-loader?{"sourceMap": true}'
# Extract styles to stand-alone css file
# Different settings for different environments can be used,
# It depends on value of NODE_ENV environment variable
# This param can also be set in webpack config:
# entry: 'bootstrap-loader/extractStyles'
extractStyles: true
# env:
# development:
# extractStyles: false
# production:
# extractStyles: true
# Customize Bootstrap variables that get imported before the original Bootstrap variables.
# Thus, derived Bootstrap variables can depend on values from here.
# See the Bootstrap _variables.scss file for examples of derived Bootstrap variables.
#
preBootstrapCustomizations: ./bootstrap-pre-customize.scss
# This gets loaded after bootstrap/variables is loaded
# Thus, you may customize Bootstrap variables
# based on the values established in the Bootstrap _variables.scss file
#
#bootstrapCustomizations: ./bootstrap-customize.scss
# Import your custom styles here
# Usually this endpoint-file contains list of @imports of your application styles
#
# appStyles: ./path/to/your/app/styles/endpoint.scss
appStyles: ../../node_modules/admin-lte/build/scss/_adminlte.raw.scss
### Bootstrap styles
styles:
# Mixins
mixins: true
# Reset and dependencies
print: true
# Core CSS
buttons: true
code: true
forms: true
grid: true
images: true
reboot: true
tables: true
type: true
# Components
alert: true
badge: true
breadcrumb: true
button-group: true
card: true
close: true
custom-forms: true
dropdown: true
input-group: true
jumbotron: true
list-group: true
media: true
nav: true
navbar: true
pagination: true
progress: true
transitions: true
# Components w/ JavaScript
carousel: true
modal: true
popover: true
tooltip: true
# Utility classes
utilities: true
### Bootstrap scripts
scripts:
alert: true
button: true
carousel: true
collapse: true
dropdown: true
modal: true
popover: true
scrollspy: true
tab: true
tooltip: true
util: true
assets/src/thirdparty/**/*.js
{
"parser": "@babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 2017,
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
},
"sourceType": "module",
"allowImportExportEverywhere": true,
"requireConfigFile": false
},
"env": {
"es6": true,
"node": true,
"cypress/globals": true
},
"plugins": [
"import",
"node",
"promise",
"standard",
"cypress",
"chai-friendly"
],
"globals": {
"document": false,
"navigator": false,
"window": false,
"$": false,
"jQuery": false,
"history": false,
"localStorage": false,
"sessionStorage": false,
"Urls": false,
"hljs": false,
"Waypoint": false,
"swh": false,
"fetch": false,
"__STATIC__": false,
"Image": false,
"nb": false,
"MathJax": false
},
"rules": {
"accessor-pairs": "error",
"arrow-spacing": [
"error",
{
"before": true,
"after": true
}
],
"block-spacing": [
"error",
"always"
],
"brace-style": [
"error",
"1tbs",
{
"allowSingleLine": true
}
],
"camelcase": [
"error",
{
"properties": "never"
}
],
"comma-dangle": [
"error",
{
"arrays": "never",
"objects": "never",
"imports": "never",
"exports": "never",
"functions": "never"
}
],
"comma-spacing": [
"error",
{
"before": false,
"after": true
}
],
"comma-style": [
"error",
"last"
],
"constructor-super": "error",
"curly": [
"error",
"multi-line"
],
"dot-location": [
"error",
"property"
],
"eol-last": "error",
"eqeqeq": [
"error",
"always",
{
"null": "ignore"
}
],
"func-call-spacing": [
"error",
"never"
],
"generator-star-spacing": [
"error",
{
"before": true,
"after": true
}
],
"handle-callback-err": [
"error",
"^(err|error)$"
],
"indent": [
"error",
2,
{
"SwitchCase": 1,
"VariableDeclarator": 1,
"outerIIFEBody": 1,
"MemberExpression": "off",
"FunctionDeclaration": {
"parameters": "first",
"body": 1
},
"FunctionExpression": {
"parameters": "first",
"body": 1
},
"CallExpression": {
"arguments": "first"
},
"ArrayExpression": "first",
"ObjectExpression": "first",
"ImportDeclaration": "first",
"flatTernaryExpressions": false,
"ignoreComments": false
}
],
"key-spacing": [
"error",
{
"beforeColon": false,
"afterColon": true
}
],
"keyword-spacing": [
"error",
{
"before": true,
"after": true
}
],
"new-cap": [
"error",
{
"newIsCap": true,
"capIsNew": false
}
],
"new-parens": "error",
"no-array-constructor": "error",
"no-caller": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-condition": [
"error",
{
"checkLoops": false
}
],
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-args": "error",
"no-dupe-class-members": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-empty-character-class": "error",
"no-empty-pattern": "error",
"no-eval": "error",
"no-ex-assign": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-boolean-cast": "error",
"no-extra-parens": [
"error",
"functions"
],
"no-fallthrough": "error",
"no-floating-decimal": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-implied-eval": "error",
"no-inner-declarations": [
"error",
"functions"
],
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-iterator": "error",
"no-label-var": "error",
"no-labels": [
"error",
{
"allowLoop": false,
"allowSwitch": false
}
],
"no-lone-blocks": "error",
"no-mixed-operators": [
"error",
{
"groups": [
[
"==",
"!=",
"===",
"!==",
">",
">=",
"<",
"<="
],
[
"&&",
"||"
],
[
"in",
"instanceof"
]
],
"allowSamePrecedence": true
}
],
"no-mixed-spaces-and-tabs": "error",
"no-multi-spaces": "error",
"no-multi-str": "error",
"no-multiple-empty-lines": [
"error",
{
"max": 1,
"maxEOF": 0
}
],
"no-negated-in-lhs": "error",
"no-new": 0,
"no-new-func": "error",
"no-new-object": "error",
"no-new-require": "error",
"no-new-symbol": "error",
"no-new-wrappers": "error",
"no-obj-calls": "error",
"no-octal": "error",
"no-octal-escape": "error",
"no-path-concat": "error",
"no-proto": "error",
"no-redeclare": "error",
"no-regex-spaces": "error",
"no-return-assign": [
"error",
"except-parens"
],
"no-return-await": "error",
"no-self-assign": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-tabs": "error",
"no-template-curly-in-string": "error",
"no-this-before-super": "error",
"no-throw-literal": "error",
"no-trailing-spaces": "error",
"no-undef": "error",
"no-undef-init": "error",
"no-unexpected-multiline": "error",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": [
"error",
{
"defaultAssignment": false
}
],
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unused-expressions": 0,
"no-unused-vars": [
"error",
{
"vars": "all",
"args": "none",
"ignoreRestSiblings": true
}
],
"no-use-before-define": [
"error",
{
"functions": false,
"classes": false,
"variables": false
}
],
"no-useless-call": "error",
"no-useless-computed-key": "error",
"no-useless-constructor": "error",
"no-useless-escape": "error",
"no-useless-rename": "error",
"no-useless-return": "error",
"no-whitespace-before-property": "error",
"no-with": "error",
"object-property-newline": [
"error",
{
"allowMultiplePropertiesPerLine": true
}
],
"one-var": [
"error",
{
"initialized": "never"
}
],
"operator-linebreak": [
"error",
"after",
{
"overrides": {
"?": "before",
":": "before"
}
}
],
"padded-blocks": [
"off",
{
"blocks": "never",
"switches": "never",
"classes": "never"
}
],
"prefer-promise-reject-errors": "error",
"prefer-const": [
"error",
{
"destructuring": "any",
"ignoreReadBeforeAssign": false
}
],
"quotes": [
"error",
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"rest-spread-spacing": [
"error",
"never"
],
"semi": [
"error",
"always"
],
"semi-spacing": [
"error",
{
"before": false,
"after": true
}
],
"space-before-blocks": [
"error",
"always"
],
"space-before-function-paren": [
"error",
"never"
],
"space-in-parens": [
"error",
"never"
],
"space-infix-ops": "error",
"space-unary-ops": [
"error",
{
"words": true,
"nonwords": false
}
],
"spaced-comment": [
"error",
"always",
{
"line": {
"markers": [
"*package",
"!",
"/",
",",
"="
]
},
"block": {
"balanced": true,
"markers": [
"*package",
"!",
",",
":",
"::",
"flow-include"
],
"exceptions": [
"*"
]
}
}
],
"symbol-description": "error",
"template-curly-spacing": [
"error",
"never"
],
"template-tag-spacing": [
"error",
"never"
],
"unicode-bom": [
"error",
"never"
],
"use-isnan": "error",
"valid-typeof": [
"error",
{
"requireStringLiterals": true
}
],
"wrap-iife": [
"error",
"any",
{
"functionPrototypeMethods": true
}
],
"yield-star-spacing": [
"error",
"both"
],
"yoda": [
"error",
"never"
],
"import/export": "off",
"import/first": "error",
"import/no-duplicates": "error",
"import/no-webpack-loader-syntax": "off",
"node/no-deprecated-api": "error",
"node/process-exit-as-throw": "error",
"promise/param-names": "error",
"standard/array-bracket-even-spacing": [
"error",
"either"
],
"standard/computed-property-even-spacing": [
"error",
"even"
],
"standard/no-callback-literal": "error",
"standard/object-curly-even-spacing": [
"error",
"either"
],
"chai-friendly/no-unused-expressions": 2,
"object-curly-spacing": [
"error",
"never"
]
}
}
\ No newline at end of file
......@@ -20,7 +20,7 @@ require('highlightjs-blade/dist/blade.min');
require('highlightjs-bqn/dist/bqn.min');
require('c3/dist/c3.min');
require('highlightjs-cairo')(hljs);
require('highlightjs-cedar/dist/hljs-cedar.min');
require('highlightjs-cedar/dist/hljs-cedar.min.js');
hljs.registerLanguage('cedar', window.hljsCedar);
require('highlightjs-chaos/dist/chaos.min');
require('highlightjs-chapel/dist/chapel.min');
......@@ -41,15 +41,18 @@ require('highlightjs-gdscript/dist/gdscript.min');
require('highlightjs-gf/dist/gf.min');
require('highlightjs-gsql/dist/gsql.min');
require('highlightjs-hlsl/dist/hlsl.min');
require('highlightjs-jsonata/dist/jsonata.min');
require('highlightjs-jolie/dist/jolie.min');
require('highlightjs-lang/dist/lang.min');
hljs.registerLanguage('lean', require('highlightjs-lean'));
require('highlightjs-liquid/dist/liquid.min');
require('highlightjs-lookml/dist/lookml.min');
require('highlightjs-luau/dist/luau.min');
hljs.registerLanguage('lox', require('highlightjs-lox'));
require('highlightjs-macaulay2/dist/macaulay2.min');
import('highlightjs-mint/dist/mint.min.js');
require('highlightjs-mirc/mirc')(hljs);
require('mirth/dist/mirth.min');
import('highlightjs-mlir/dist/mlir.min.js');
hljs.registerLanguage('modelica', require('highlightjs-modelica'));
require('highlightjs-motoko')(hljs);
......@@ -59,6 +62,8 @@ import('highlightjs-ocl/dist/ocl.min.js');
hljs.registerLanguage('octave', require('highlightjs-octave').default);
require('highlightjs-oz/dist/oz.min');
require('hightlightjs-papyrus/dist/papyrus.min');
require('highlightjs-phix/src/languages/phix')(hljs);
require('highlightjs-poweron/dist/poweron.min');
require('highlightjs-qsharp/dist/qsharp.min');
require('highlightjs-redbol/dist/redbol.min');
import('highlightjs-rescript/dist/rescript.min.js');
......@@ -71,10 +76,12 @@ hljs.registerLanguage('sdml', global.sdml);
require('highlightjs-sfz/dist/sfz.min');
require('highlightjs-solidity/dist/solidity.min');
require('highlightjs-structured-text/dist/iecst.min');
require('highlightjs-svelte/dist/svelte.min');
import('highlight.svelte/dist/svelte.min.js');
require('highlightjs-terraform')(hljs);
require('highlight.js-tsql/dist/tsql.min');
require('highlightjs-unison/dist/unison.min');
require('highlightjs-vba/dist/vba.min');
require('highlightjs-wgsl/dist/wgsl.min');
require('highlightjs-xsharp/dist/xsharp.min');
require('highlightjs-zenscript/dist/zenscript.min');
require('highlightjs-zig/dist/zig.min');
......@@ -92,12 +99,12 @@ class DumpHighlightjsLanguagesDataPlugin {
compiler.hooks.done.tap('DumpHighlightjsLanguagesDataPlugin', statsObj => {
const outputPath = statsObj.compilation.compiler.outputPath;
const hljsDataFile = path.join(outputPath, 'json/highlightjs-languages.json');
const languages = hljs.listLanguages().sort();
const languages = hljs.listLanguages().concat(['odin', 'ttcn3']).sort();
const hljsLanguagesData = {'languages': languages};
const languageAliases = {};
for (const language of languages) {
const languageData = hljs.getLanguage(language);
if (!languageData.hasOwnProperty('aliases')) {
if (languageData === undefined || !languageData.hasOwnProperty('aliases')) {
continue;
}
for (const alias of languageData.aliases) {
......
/**
* Copyright (C) 2018-2024 The Software Heritage developers
* Copyright (C) 2018-2025 The Software Heritage developers
* See the AUTHORS file at the top-level directory of this distribution
* License: GNU Affero General Public License version 3, or any later version
* See top-level LICENSE file for more information
......@@ -9,6 +9,7 @@
// import required node modules and webpack plugins
const chalk = require('chalk');
const {execSync} = require('child_process');
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
......@@ -33,14 +34,34 @@ const devServerPublicPath = 'http://localhost:' + devServerPort + '/static/';
// our assets or not
const publicPath = isDevServer ? devServerPublicPath : '/static/';
const nodeModules = path.resolve(__dirname, '../../node_modules/');
const repoRootPath = path.resolve(__dirname, '../..');
const nodeModules = path.resolve(repoRootPath, 'node_modules');
let outputPath;
try {
// handle editable install of swh.web package as compiled assets must
// be outputted in the swh.web.static sub-package but it is located in
// a non trivial location in that case
const swhWebPythonModulePath = execSync(
'python -c "from importlib.metadata import files;' +
'pth_file = [p for p in files(\'swh.web\') if str(p).endswith(\'.pth\')];' +
'swh_web_package_path = pth_file[0].read_text()[:-1];' +
'assert swh_web_package_path.startswith(\'/\');' +
'print(swh_web_package_path, end=\'\')"',
{cwd: __dirname, stdio: ['pipe', 'pipe', null]}
);
outputPath = path.resolve(swhWebPythonModulePath.toString(), './swh/web/static');
} catch (_) {
// assets should be outputted in swh/web/static folder otherwise
outputPath = path.resolve('./swh/web/static/');
}
// collect all bundles we want to produce with webpack,
// bundles will be generated by scanning swh-web django applications:
// * if swh/web/<app>/assets/index.js exists, bundle <app> is generated
// * if swh/web/<app>/assets/<bundle>/index.js exists, bundle <bundle> is generated
var bundles = {};
const appsDir = path.join(__dirname, '../../swh/web');
const appsDir = path.join(repoRootPath, 'swh/web');
fs.readdirSync(appsDir).forEach(app => {
const appAssetsDir = path.join(appsDir, app, 'assets');
if (fs.existsSync(appAssetsDir)) {
......@@ -87,13 +108,13 @@ const cssLoaders = [
{
loader: 'css-loader',
options: {
sourceMap: !isDevServer
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: !isDevServer,
sourceMap: true,
postcssOptions: {
plugins: [
// automatically add vendor prefixes to css rules
......@@ -150,7 +171,7 @@ module.exports = {
'Access-Control-Allow-Origin': '*'
},
watchFiles: {
paths: ['assets/**/*', 'static/**/*', 'swh/web/**/*'],
paths: ['assets/**/*', 'swh/web/**/*'],
options: {
ignored: /.*.sqlite3.*/
}
......@@ -160,7 +181,7 @@ module.exports = {
entry: bundles,
// assets output configuration
output: {
path: path.resolve('./static/'),
path: outputPath,
filename: 'js/[name].[contenthash].js',
chunkFilename: 'js/[name].[contenthash].js',
publicPath: publicPath,
......@@ -174,8 +195,8 @@ module.exports = {
// configure base paths for resolving modules with webpack
modules: [
'node_modules',
path.resolve(__dirname, '../src'),
path.resolve(__dirname, '../../swh/web')
path.resolve(repoRootPath, 'assets/src'),
path.resolve(repoRootPath, 'swh/web')
]
},
stats: 'errors-warnings',
......@@ -281,10 +302,21 @@ module.exports = {
{
test: /\.scss$/,
use: cssLoaders.concat([
{
loader: 'resolve-url-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: !isDevServer
sourceMap: true,
implementation: require('sass'),
sassOptions: {
quietDeps: true,
silenceDeprecations: ['import', 'global-builtin']
}
}
}
])
......@@ -351,7 +383,7 @@ module.exports = {
}),
// needed in order to use django_webpack_loader
new BundleTracker({
path: './static/',
path: outputPath,
filename: 'webpack-stats.json'
}),
// for generating the robots.txt file
......@@ -372,51 +404,35 @@ module.exports = {
new webpack.DefinePlugin({
__STATIC__: JSON.stringify(publicPath)
}),
// needed in order to use bootstrap 4.x
new webpack.ProvidePlugin({
Popper: ['popper.js', 'default'],
Alert: 'exports-loader?Alert!bootstrap/js/dist/alert',
Button: 'exports-loader?Button!bootstrap/js/dist/button',
Carousel: 'exports-loader?Carousel!bootstrap/js/dist/carousel',
Collapse: 'exports-loader?Collapse!bootstrap/js/dist/collapse',
Dropdown: 'exports-loader?Dropdown!bootstrap/js/dist/dropdown',
Modal: 'exports-loader?Modal!bootstrap/js/dist/modal',
Popover: 'exports-loader?Popover!bootstrap/js/dist/popover',
Scrollspy: 'exports-loader?Scrollspy!bootstrap/js/dist/scrollspy',
Tab: 'exports-loader?Tab!bootstrap/js/dist/tab',
Tooltip: 'exports-loader?Tooltip!bootstrap/js/dist/tooltip',
Util: 'exports-loader?Util!bootstrap/js/dist/util'
}),
// needed in order to use pdf.js
new webpack.IgnorePlugin({resourceRegExp: /^\.\/pdf.worker.js$/}),
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(nodeModules, 'pdfjs-dist/legacy/build/pdf.worker.min.mjs'),
to: path.resolve(__dirname, '../../static/js/pdf.worker.min.js')
to: path.resolve(outputPath, 'js/pdf.worker.min.js')
},
{
from: path.resolve(nodeModules, 'mathjax/es5/output/chtml/fonts/woff-v2/**'),
to: path.resolve(__dirname, '../../static/fonts/[name][ext]')
to: path.resolve(outputPath, 'fonts/[name][ext]')
},
{
from: path.resolve(nodeModules, 'mathjax/es5/input/tex/extensions/'),
to: path.resolve(__dirname, '../../static/js/mathjax/input/tex/extensions/')
to: path.resolve(outputPath, 'js/mathjax/input/tex/extensions/')
},
{
from: path.resolve(nodeModules, 'mathjax/es5/input/mml/extensions/'),
to: path.resolve(__dirname, '../../static/js/mathjax/input/mml/extensions/')
to: path.resolve(outputPath, 'js/mathjax/input/mml/extensions/')
},
{
from: path.resolve(nodeModules, 'mathjax/es5/sre/mathmaps/'),
to: path.resolve(__dirname, '../../static/js/mathjax/sre/mathmaps/')
to: path.resolve(outputPath, 'js/mathjax/sre/mathmaps/')
}
]
}),
new GenerateWebLabelsPlugin({
outputType: 'json',
exclude: ['mini-css-extract-plugin',
'bootstrap-loader'],
exclude: ['mini-css-extract-plugin'],
srcReplace: {
'./node_modules/admin-lte/dist/js/adminlte.min.js':
'./node_modules/admin-lte/dist/js/adminlte.js'
......@@ -482,8 +498,8 @@ module.exports = {
// Process all js files with eslint for consistent code style
// and avoid bad js development practices.
new ESLintPlugin({
overrideConfigFile: path.join(__dirname, '.eslintrc'),
ignorePath: path.join(__dirname, '.eslintignore'),
configType: 'flat',
overrideConfigFile: path.join(repoRootPath, 'eslint.config.js'),
cache: true,
emitWarning: true
}),
......@@ -506,10 +522,11 @@ module.exports = {
'build/**/*.css',
'build/**/*.scss',
'cypress/**/*.css',
'assets/config/*.scss',
'swh/web/**/*.scss',
'assets/src/thirdparty/**/*.css',
'docs/**/*.css',
'.cypress_cache/**/*.css']
'.cypress_cache/**/*.css',
'swh/web/static/css/*.css']
}
})
],
......
......@@ -9,7 +9,7 @@
// import required webpack plugins
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const sentryWebpackPlugin = require('@sentry/webpack-plugin').sentryWebpackPlugin;
const shelljs = require('shelljs');
......@@ -26,8 +26,8 @@ webpackProdConfig.optimization.minimizer = [
parallel: true
}),
// use cssnano for minimizing css and generate source map files
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
new CssMinimizerPlugin({
minimizerOptions: {
map: {
inline: false,
annotation: true
......@@ -46,7 +46,7 @@ webpackProdConfig.plugins.push(sentryWebpackPlugin({
url: 'https://sentry.softwareheritage.org/',
dryRun: process.env.SENTRY_AUTH_TOKEN === undefined,
sourcemaps: {
assets: ['static/js/**']
assets: ['swh/web/static/js/**']
},
release: {
name: shelljs.exec('git describe --abbrev=0', {silent: true}).stdout.slice(1, -1)
......
/**
* Copyright (C) 2018-2022 The Software Heritage developers
* Copyright (C) 2018-2025 The Software Heritage developers
* See the AUTHORS file at the top-level directory of this distribution
* License: GNU Affero General Public License version 3, or any later version
* See top-level LICENSE file for more information
......@@ -83,14 +83,18 @@ export function selectText(startNode, endNode) {
selection.addRange(range);
}
export function textToHTML(text) {
const textArea = document.createElement('textarea');
textArea.innerText = text;
return textArea.innerHTML;
}
export function htmlAlert(type, message, closable = false) {
let closeButton = '';
let extraClasses = '';
if (closable) {
closeButton =
`<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>`;
`<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>`;
extraClasses = 'alert-dismissible';
}
return `<div class="alert alert-${type} ${extraClasses}" role="alert">${message}${closeButton}</div>`;
......@@ -120,13 +124,12 @@ export async function isArchivedOrigin(originPath, visitType) {
// Not a valid URL, return immediately
return false;
} else {
const response = await fetch(Urls.api_1_origin(originPath));
if (!response.ok || response.status !== 200) {
return false;
} else {
const originData = await response.json();
return !visitType || visitType === 'any' || originData.visit_types.includes(visitType);
let url = `${Urls.api_1_origin_visit_latest(originPath)}?require_snapshot=true`;
if (visitType && visitType !== 'any') {
url += `&visit_type=${visitType}`;
}
const response = await fetch(url);
return response.ok;
}
}
......
......@@ -41,14 +41,17 @@ import 'highlightjs-gf/dist/gf.min';
import 'highlightjs-gsql/dist/gsql.min';
import 'highlightjs-hlsl/dist/hlsl.min';
import 'highlightjs-jolie/dist/jolie.min';
import 'highlightjs-jsonata/dist/jsonata.min';
import 'highlightjs-lang/dist/lang.min';
import * as hljsDefineLean from 'highlightjs-lean';
import 'highlightjs-liquid/dist/liquid.min';
import 'highlightjs-lookml/dist/lookml.min';
import 'highlightjs-luau/dist/luau.min';
import {default as hljsDefineLox} from 'highlightjs-lox';
import 'highlightjs-macaulay2/dist/macaulay2.min';
import 'highlightjs-mint/dist/mint.min';
import 'script-loader!highlightjs-mirc/mirc';
import 'mirth/dist/mirth.min';
import 'highlightjs-mlir/dist/mlir.min';
import * as hljsDefineModelica from 'highlightjs-modelica';
import {motoko, candid} from 'highlightjs-motoko';
......@@ -56,8 +59,11 @@ import 'highlightjs-never/dist/never.min';
import 'highlightjs-oak/dist/oak.min';
import 'highlightjs-ocl/dist/ocl.min';
import {default as hljsDefineOctave} from 'highlightjs-octave';
import {default as hljsDefineOdin} from 'highlightjs-odin/dist/odin.min';
import 'highlightjs-oz/dist/oz.min';
import 'hightlightjs-papyrus/dist/papyrus.min';
import 'highlightjs-phix/src/languages/phix';
import 'highlightjs-poweron/dist/poweron.min';
import 'highlightjs-qsharp/dist/qsharp.min';
import 'highlightjs-redbol/dist/redbol.min';
import 'highlightjs-rescript/dist/rescript.min';
......@@ -69,11 +75,14 @@ import 'highlightjs-sdml/src/language/sdml';
import 'highlightjs-sfz/dist/sfz.min';
import 'highlightjs-solidity/dist/solidity.min';
import 'highlightjs-structured-text/dist/iecst.min';
import 'highlightjs-svelte/dist/svelte.min';
import 'highlight.svelte/dist/svelte.min';
import 'script-loader!highlightjs-terraform';
import 'highlight.js-tsql/dist/tsql.min';
import 'highlightjs-xsharp/dist/xsharp.min';
import {default as hljsDefineTTCN3} from 'highlightjs-ttcn3';
import 'highlightjs-unison/dist/unison.min';
import 'highlightjs-vba/dist/vba.min';
import 'highlightjs-wgsl/dist/wgsl.min';
import 'highlightjs-xsharp/dist/xsharp.min';
import 'highlightjs-zenscript/dist/zenscript.min';
import 'highlightjs-zig/dist/zig.min';
......@@ -104,7 +113,9 @@ hljs.registerLanguage('mirc', window.hljsDefineMIRC);
hljs.registerLanguage('modelica', hljsDefineModelica);
hljs.registerLanguage('motoko', motoko);
hljs.registerLanguage('octave', hljsDefineOctave);
hljs.registerLanguage('odin', hljsDefineOdin);
hljs.registerLanguage('robot', window.hljsDefineRobot);
hljs.registerLanguage('rpm-specfile', window.hljsDefineRpmSpecfile);
hljs.registerLanguage('sdml', window.sdml);
hljs.registerLanguage('terraform', window.hljsDefineTerraform);
hljs.registerLanguage('ttcn3', hljsDefineTTCN3);
......@@ -4,14 +4,14 @@
License: GNU Affero General Public License version 3, or any later version
See top-level LICENSE file for more information
%>
<div class="custom-control custom-checkbox swhid-option">
<input class="custom-control-input" value="option-user-requests-filter"
<div class="form-check swhid-option">
<input class="form-check-input" value="option-user-requests-filter"
type="checkbox"
<% if (checked) { %>
checked="checked"
<% } %>
id="<%= inputId %>">
<label class="custom-control-label font-weight-normal" for="<%= inputId %>">
<label class="form-check-label font-weight-normal" for="<%= inputId %>">
show only your own requests
</label>
</div>
......@@ -2,7 +2,12 @@ const {defineConfig} = require('cypress');
module.exports = defineConfig({
projectId: 'swh-web',
video: false,
// workaround performance regression in Cypress 14
// https://github.com/cypress-io/cypress/issues/30980
video: true,
videosFolder: '/tmp/cypress/videos/',
viewportWidth: 1920,
viewportHeight: 1080,
defaultCommandTimeout: 20000,
......
......@@ -9,7 +9,8 @@ const pagesToCheck = [
{name: 'homepage', path: '/'},
{name: 'coverage', path: '/coverage/'},
{name: 'browse origin directory', path: '/browse/origin/directory/?origin_url=https://github.com/memononen/libtess2'},
{name: 'browse origin content', path: '/browse/content/sha1_git:32a56bf4060402c477271380880ef01ba36ea5b1/?origin_url=https://github.com/memononen/libtess2&path=Source/sweep.c'}
{name: 'browse origin content', path: '/browse/content/sha1_git:32a56bf4060402c477271380880ef01ba36ea5b1/?origin_url=https://github.com/memononen/libtess2&path=Source/sweep.c'},
{name: 'content-policies', path: '/content-policies/'}
];
describe('Accessibility compliance tests', function() {
......
/**
* Copyright (C) 2022-204 The Software Heritage developers
* Copyright (C) 2022-2024 The Software Heritage developers
* See the AUTHORS file at the top-level directory of this distribution
* License: GNU Affero General Public License version 3, or any later version
* See top-level LICENSE file for more information
......@@ -19,7 +19,7 @@ function populateForm(type, url, contact, email, consent, comment) {
}
function submitForm() {
cy.get('#requestCreateForm input[type=submit]').click();
cy.get('#swh-input-form-submit').click();
cy.get('#requestCreateForm').then($form => {
if ($form[0].checkValidity()) {
cy.wait('@addForgeRequestCreate');
......@@ -62,8 +62,11 @@ describe('Browse requests list tests', function() {
cy.user2Login();
cy.visit(this.addForgeNowUrl);
// Disable checking if the example GitLab has public repos
cy.get('#swh-input-forge-validate').invoke('val', 'off');
// create requests for the user 'user'
populateForm('gitlab', 'https://gitlab.org', 'admin', 'admin@example.org', 'on', '');
populateForm('gitlab', 'https://gitlab.example.org/', 'admin', 'admin@example.org', 'on', '');
submitForm();
// user requests filter checkbox should be in the DOM
......@@ -84,9 +87,9 @@ describe('Browse requests list tests', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm('gitea', 'https://gitea.org', 'admin', 'admin@example.org', 'on', '');
populateForm('gitea', 'https://gitea.example.org/', 'admin', 'admin@example.org', 'on', '');
submitForm();
populateForm('cgit', 'https://cgit.org', 'admin', 'admin@example.org', 'on', '');
populateForm('cgit', 'https://cgit.example.org/', 'admin', 'admin@example.org', 'on', '');
submitForm();
// user requests filter checkbox should be in the DOM
......@@ -129,7 +132,7 @@ describe('Browse requests list tests', function() {
});
it('should display search link when first forge origin has been loaded', function() {
const forgeUrl = 'https://cgit.example.org';
const forgeUrl = 'https://cgit.example.org/';
cy.intercept(this.listAddForgeRequestsUrl + '**', {body: {
'recordsTotal': 1,
'draw': 1,
......@@ -290,17 +293,17 @@ describe('Test add-forge-request creation', function() {
it('should update browse list on successful submission', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm('bitbucket', 'https://gitlab.com', 'test', 'test@example.com', 'on', 'test comment');
populateForm('bitbucket', 'https://gitlab.example.com/', 'test', 'test@example.com', 'on', 'test comment');
submitForm();
cy.visit(this.addForgeNowUrl);
cy.get('#swh-add-forge-requests-list-tab').click();
// click the link to the list in the form footer
cy.get('#swh-show-forge-add-requests-list').click();
cy.wait('@addForgeRequestsList');
cy.get('#add-forge-request-browse')
.should('be.visible')
.should('contain', 'gitlab.com');
.should('contain', 'gitlab.example.com');
cy.get('#add-forge-request-browse')
.should('be.visible')
......@@ -310,13 +313,13 @@ describe('Test add-forge-request creation', function() {
it('should show error message on conflict', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm('bitbucket', 'https://gitlab.com', 'test', 'test@example.com', 'on', 'test comment');
populateForm('bitbucket', 'https://gitlab.example.com/', 'test', 'test@example.com', 'on', 'test comment');
submitForm();
submitForm(); // Submitting the same data again
cy.get('#userMessage')
.should('have.class', 'badge-danger')
.should('have.class', 'text-bg-danger')
.should('contain', 'already exists');
});
......@@ -336,16 +339,27 @@ describe('Test add-forge-request creation', function() {
});
it('should not validate form when forge URL is invalid', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm('bitbucket', 'bitbucket.org', 'test', 'test@example.com', 'on', 'test comment');
submitForm();
cy.get('#swh-input-forge-url')
.then(input => {
assert.isFalse(input[0].checkValidity());
});
const invalidForgeInputData = [
['bitbucket', 'bitbucket.example.org'], // missing URL scheme
['gitlab', 'https://gitlab.example.com'], // missing trailing slash
['gitea', 'https://gitea.example.com/explore/repos'], // not a base URL
['gitlab', 'https://gitlab.com/user/project'], // gitlab repo URL, not a new forge
['gitlab', 'https://github.com/user/project'] // github repo URL, not a forge
];
invalidForgeInputData.forEach(forgeInputData => {
const [forgeType, forgeURL] = forgeInputData;
it(`should not validate form for such invalid forge URL: ${forgeURL}`, function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm(forgeType, forgeURL, 'test', 'test@example.com', 'on', 'test comment');
submitForm();
cy.get('#swh-input-forge-url')
.then(input => {
assert.isFalse(input[0].checkValidity());
});
});
});
});
......@@ -109,7 +109,7 @@ describe('Test add forge now request dashboard load', function() {
.should('contain', 'bitbucket');
cy.get('#requestURL a')
.should('have.attr', 'href', 'http://test.example.com');
.should('have.attr', 'href', 'https://test.example.com');
cy.get('#requestContactEmail')
.should('contain', 'test@example.com');
......@@ -191,7 +191,7 @@ describe('Test add forge now request dashboard load', function() {
function populateAndSubmitForm() {
cy.get('#decisionOptions').select('WAITING_FOR_FEEDBACK');
cy.get('#updateComment').type('This is an update comment');
cy.get('#updateRequestForm button[type=submit]').click();
cy.get('#updateRequestForm #update-request-submit').click();
}
describe('Test add forge now request update', function() {
......@@ -228,8 +228,8 @@ describe('Test add forge now request update', function() {
cy.wait('@forgeRequestUpdate');
cy.get('#userMessage')
.should('contain', 'The request status has been updated')
.should('not.have.class', 'badge-danger')
.should('have.class', 'badge-success');
.should('not.have.class', 'text-bg-danger')
.should('have.class', 'text-bg-success');
});
it('should update the dashboard after submit', function() {
......@@ -300,12 +300,12 @@ describe('Test add forge now request update', function() {
{forceNetworkError: true})
.as('updateFailedRequest');
cy.get('#updateComment').type('This is an update comment');
cy.get('#updateRequestForm button[type=submit]').click();
cy.get('#updateRequestForm #update-request-submit').click();
cy.wait('@updateFailedRequest');
cy.get('#userMessage')
.should('contain', 'Sorry; Updating the request failed')
.should('have.class', 'badge-danger')
.should('not.have.class', 'badge-success');
.should('have.class', 'text-bg-danger')
.should('not.have.class', 'text-bg-success');
});
});