From 167dbd9b7aae0d101cfb749917c674f1fcd860d6 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 3 Nov 2020 13:53:01 +0000 Subject: [PATCH] Initial commit --- .circleci/config.yml | 81 +++++++++ .circleci/trigger-nightly-build.sh | 8 + .coveragerc | 24 +++ .eslintrc.js | 24 +++ .gitignore | 12 ++ .prettierrc.toml | 2 + .travis.yml | 44 +++++ LICENSE | 33 ++++ MANIFEST.in | 4 + package.json | 45 +++++ setup.py | 47 ++++++ testmanage.py | 66 ++++++++ tox.ini | 41 +++++ tsconfig.json | 15 ++ wagtail_ab_testing/__init__.py | 1 + wagtail_ab_testing/apps.py | 7 + .../static/wagtail_ab_testing/js/.gitignore | 1 + wagtail_ab_testing/static_src/custom.d.ts | 73 ++++++++ wagtail_ab_testing/static_src/main.tsx | 0 wagtail_ab_testing/test/__init__.py | 1 + wagtail_ab_testing/test/apps.py | 7 + wagtail_ab_testing/test/settings.py | 157 ++++++++++++++++++ wagtail_ab_testing/test/tests/__init__.py | 0 wagtail_ab_testing/test/urls.py | 15 ++ wagtail_ab_testing/wagtail_hooks.py | 24 +++ webpack.config.js | 52 ++++++ 26 files changed, 784 insertions(+) create mode 100644 .circleci/config.yml create mode 100755 .circleci/trigger-nightly-build.sh create mode 100644 .coveragerc create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .prettierrc.toml create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 package.json create mode 100644 setup.py create mode 100644 testmanage.py create mode 100644 tox.ini create mode 100644 tsconfig.json create mode 100644 wagtail_ab_testing/__init__.py create mode 100644 wagtail_ab_testing/apps.py create mode 100644 wagtail_ab_testing/static/wagtail_ab_testing/js/.gitignore create mode 100644 wagtail_ab_testing/static_src/custom.d.ts create mode 100644 wagtail_ab_testing/static_src/main.tsx create mode 100644 wagtail_ab_testing/test/__init__.py create mode 100644 wagtail_ab_testing/test/apps.py create mode 100644 wagtail_ab_testing/test/settings.py create mode 100644 wagtail_ab_testing/test/tests/__init__.py create mode 100644 wagtail_ab_testing/test/urls.py create mode 100644 wagtail_ab_testing/wagtail_hooks.py create mode 100644 webpack.config.js diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..5a0e65c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,81 @@ +version: 2 + +jobs: + flake8: + docker: + - image: circleci/python:3.8 + steps: + - checkout + - run: pip install flake8 + - run: flake8 wagtail_ab_testing + + prettier: + docker: + - image: circleci/node:14 + steps: + - checkout: + + - type: cache-restore + keys: + - node-modules-{{ .Branch }}-{{ checksum "package-lock.json" }} + - node-modules-{{ .Branch }}- + - node-modules-master- + + - run: npm install + - run: npm run lint + + - type: cache-save + key: node-modules-{{ .Branch }}-{{ checksum "package-lock.json" }} + paths: + - "node_modules" + + test: + docker: + - image: circleci/python:3.8 + steps: + - checkout + + - type: cache-restore + keys: + - pip-{{ .Branch }}- + - pip-master- + + - run: pip install -e .[testing] + + - type: cache-save + key: pip-{{ .Branch }}-{{ epoch }} + paths: + - "~/.cache/pip" + + - run: python testmanage.py test + + nightly-wagtail-test: + docker: + - image: circleci/python:3.8 + steps: + - checkout + - run: git clone git@github.com:wagtail/wagtail.git + + - run: pip install -e .[testing] + - run: pip install ./wagtail + + - run: python testmanage.py test + +workflows: + version: 2 + test: + jobs: + - flake8 + - prettier + - test + + nightly: + jobs: + - nightly-wagtail-test + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - master diff --git a/.circleci/trigger-nightly-build.sh b/.circleci/trigger-nightly-build.sh new file mode 100755 index 0000000..35beb09 --- /dev/null +++ b/.circleci/trigger-nightly-build.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Triggers a test run against the master version of Wagtail + +# This job will is scheduled in the config.yml, this script is here to help test the job + +curl -u ${CIRCLE_API_USER_TOKEN}: \ + -d build_parameters[CIRCLE_JOB]=nightly-wagtail-test \ + https://circleci.com/api/v1.1/project/github/wagtail/wagtail-ab-testing}/tree/master diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..a86701e --- /dev/null +++ b/.coveragerc @@ -0,0 +1,24 @@ +[run] +branch = True +include = wagtail_ab_testing/* +omit = */migrations/*,*/tests/* + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: + +ignore_errors = True diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..793d978 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,24 @@ +module.exports = { + parser: '@typescript-eslint/parser', + extends: [ + 'torchbox', + 'plugin:@typescript-eslint/recommended', + ], + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + }, + rules: { + '@typescript-eslint/explicit-member-accessibility': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + 'react/jsx-filename-extension': [1, { 'extensions': ['.jsx', '.tsx'] }], + }, + settings: { + 'import/resolver': { + 'node': { + 'extensions': ['.js', '.jsx', '.ts', '.tsx'] + } + } + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ee18de --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.pyc +/build +/dist +/wagtail_ab_testing.egg-info +/.coverage +/htmlcov +/.tox +/venv +/.vscode +/site +/test_wagtail_ab_testing.db +/node_modules diff --git a/.prettierrc.toml b/.prettierrc.toml new file mode 100644 index 0000000..009474b --- /dev/null +++ b/.prettierrc.toml @@ -0,0 +1,2 @@ +tabWidth = 4 +singleQuote = true diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0b769e4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,44 @@ +dist: focal +language: python +cache: pip + +services: + - postgresql + +matrix: + include: + - env: TOXENV=py37-dj22-wamaster-postgres + python: 3.7 + + - env: TOXENV=py38-dj22-wamaster-postgres + python: 3.8 + + - env: TOXENV=py38-dj22-wamaster-sqlite + python: 3.8 + + - env: TOXENV=py37-dj30-wamaster-postgres + python: 3.7 + + - env: TOXENV=py38-dj30-wamaster-postgres + python: 3.8 + + - env: TOXENV=py38-dj31-wamaster-postgres + python: 3.8 + + - env: TOXENV=py38-dj31-wamaster-sqlite + python: 3.8 + +# Not currently needed as we test against Wagtail master anyway +# When Wagtail 2.11 is released and the above environments are updated. Uncomment this. +# +# - env: TOXENV=py38-dj31-wamaster-postgres +# python: 3.8 +# +# allow_failures: +# - env: TOXENV=py38-dj31-wamaster-postgres + +install: + - pip install tox + +script: + - tox diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e11a878 --- /dev/null +++ b/LICENSE @@ -0,0 +1,33 @@ + + + +BSD License + +Copyright (c) 2020, Karl Hobley +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e486d42 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include LICENSE *.rst *.txt *.md +graft wagtail_ab_testing +global-exclude __pycache__ +global-exclude *.py[co] diff --git a/package.json b/package.json new file mode 100644 index 0000000..bf49e17 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "wagtail-ab-testing", + "version": "1.0.0", + "description": "", + "main": "wagtail_ab_testing/static_src/main.tsx", + "scripts": { + "build": "webpack --mode=production", + "start": "webpack --mode=development --watch", + "format": "prettier --write wagtail_ab_testing/**/*.{ts,tsx,scss}", + "lint": "prettier --check wagtail_ab_testing/**/*.{ts,tsx,scss}" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@svgr/webpack": "^5.4.0", + "@types/react": "^16.8.19", + "@types/react-dom": "^16.8.19", + "@types/styled-components": "^5.1.2", + "@typescript-eslint/eslint-plugin": "^1.12.0", + "@typescript-eslint/parser": "^1.12.0", + "css-loader": "^2.1.1", + "eslint": "^5.16.0", + "eslint-config-airbnb": "^17.1.1", + "eslint-config-prettier": "^6.0.0", + "eslint-config-torchbox": "^0.2.0", + "eslint-plugin-import": "^2.18.0", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-react": "^7.14.2", + "file-loader": "^3.0.1", + "node-sass": "^4.13.1", + "prettier": "1.18.2", + "sass": "^1.20.3", + "sass-loader": "^7.1.0", + "style-loader": "^0.23.1", + "ts-loader": "^6.0.2", + "typescript": "^3.5.1", + "webpack": "^4.32.2", + "webpack-cli": "^3.3.2" + }, + "dependencies": { + "react": "^16.13.1", + "react-dom": "^16.13.1", + "styled-components": "^5.1.1" + } +} diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9b57c42 --- /dev/null +++ b/setup.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +import sys, os + +from setuptools import setup, find_packages + +# Hack to prevent "TypeError: 'NoneType' object is not callable" error +# in multiprocessing/util.py _exit_function when setup.py exits +# (see http://www.eby-sarna.com/pipermail/peak/2010-May/003357.html) +try: + import multiprocessing +except ImportError: + pass + + +setup( + name="wagtail-ab-testing", + version="0.1", + description="A/B Testing for Wagtail", + author="Karl Hobley", + author_email="karl@torchbox.com", + url="", + packages=find_packages(), + include_package_data=True, + license="BSD", + classifiers=[ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Framework :: Django", + "Framework :: Django :: 2.2", + "Framework :: Django :: 3.0", + "Framework :: Django :: 3.1", + "Framework :: Wagtail", + "Framework :: Wagtail :: 2", + ], + install_requires=["Django>=2.2,<3.2", "Wagtail>=2.11,<2.12"], + extras_require={ + "testing": ["dj-database-url==0.5.0", "freezegun==0.3.15"], + }, + zip_safe=False, +) diff --git a/testmanage.py b/testmanage.py new file mode 100644 index 0000000..581d8e5 --- /dev/null +++ b/testmanage.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +import argparse +import os +import shutil +import sys +import warnings + +from django.core.management import execute_from_command_line + + +os.environ["DJANGO_SETTINGS_MODULE"] = "wagtail_ab_testing.test.settings" + + +def make_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--deprecation", + choices=["all", "pending", "imminent", "none"], + default="imminent", + ) + return parser + + +def parse_args(args=None): + return make_parser().parse_known_args(args) + + +def runtests(): + args, rest = parse_args() + + only_wagtail = r"^wagtail(\.|$)" + if args.deprecation == "all": + # Show all deprecation warnings from all packages + warnings.simplefilter("default", DeprecationWarning) + warnings.simplefilter("default", PendingDeprecationWarning) + elif args.deprecation == "pending": + # Show all deprecation warnings from wagtail + warnings.filterwarnings( + "default", category=DeprecationWarning, module=only_wagtail + ) + warnings.filterwarnings( + "default", category=PendingDeprecationWarning, module=only_wagtail + ) + elif args.deprecation == "imminent": + # Show only imminent deprecation warnings from wagtail + warnings.filterwarnings( + "default", category=DeprecationWarning, module=only_wagtail + ) + elif args.deprecation == "none": + # Deprecation warnings are ignored by default + pass + + argv = [sys.argv[0]] + rest + + try: + execute_from_command_line(argv) + finally: + from wagtail.tests.settings import STATIC_ROOT, MEDIA_ROOT + + shutil.rmtree(STATIC_ROOT, ignore_errors=True) + shutil.rmtree(MEDIA_ROOT, ignore_errors=True) + + +if __name__ == "__main__": + runtests() diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..277ab1d --- /dev/null +++ b/tox.ini @@ -0,0 +1,41 @@ +[tox] +skipsdist = True +usedevelop = True + +envlist = py{37,38}-dj{22,30,31,master}-wa{211,master}-{sqlite,postgres} + +[flake8] +# E501: Line too long +# W503: line break before binary operator (superseded by W504 line break after binary operator) +ignore = E501,W503 +exclude = migrations,node_modules + +[testenv] +install_command = pip install -e ".[testing]" -U {opts} {packages} +commands = coverage run testmanage.py test --deprecation all + +basepython = + py37: python3.7 + py38: python3.8 + +deps = + coverage + + dj22: Django>=2.2,<2.3 + dj30: Django>=3.0,<3.1 + dj31: Django>=3.1,<3.2 + djmaster: git+https://github.com/django/django.git@master#egg=Django + djmaster: git+https://github.com/wagtail/django-modelcluster.git + + wa211: wagtail>=2.11,<2.12 + wamaster: git+https://github.com/wagtail/wagtail.git + + postgres: psycopg2>=2.6 + +setenv = + postgres: DATABASE_URL=postgres:///wagtail_ab_testing + +[testenv:flake8] +basepython=python3.7 +deps=flake8>=2.2.0 +commands=flake8 wagtail_ab_testing diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b555e52 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "jsx": "react", + "lib": ["es2015", "dom"], + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictNullChecks": true, + "esModuleInterop": true + }, + "files": [ + "wagtail_ab_testing/static_src/main.tsx", + "wagtail_ab_testing/static_src/custom.d.ts" + ] +} diff --git a/wagtail_ab_testing/__init__.py b/wagtail_ab_testing/__init__.py new file mode 100644 index 0000000..6d160f3 --- /dev/null +++ b/wagtail_ab_testing/__init__.py @@ -0,0 +1 @@ +default_app_config = "wagtail_ab_testing.apps.WagtailAbTestingAppConfig" diff --git a/wagtail_ab_testing/apps.py b/wagtail_ab_testing/apps.py new file mode 100644 index 0000000..6699c4d --- /dev/null +++ b/wagtail_ab_testing/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class WagtailAbTestingAppConfig(AppConfig): + label = "wagtail_ab_testing" + name = "wagtail_ab_testing" + verbose_name = "A/B Testing" diff --git a/wagtail_ab_testing/static/wagtail_ab_testing/js/.gitignore b/wagtail_ab_testing/static/wagtail_ab_testing/js/.gitignore new file mode 100644 index 0000000..9816ee3 --- /dev/null +++ b/wagtail_ab_testing/static/wagtail_ab_testing/js/.gitignore @@ -0,0 +1 @@ +/wagtail-ab-testing.js diff --git a/wagtail_ab_testing/static_src/custom.d.ts b/wagtail_ab_testing/static_src/custom.d.ts new file mode 100644 index 0000000..0515baa --- /dev/null +++ b/wagtail_ab_testing/static_src/custom.d.ts @@ -0,0 +1,73 @@ +export {}; + +// Allows SVG files to be imported and used in TypeScript +declare module '*.svg' { + const content: any; + export default content; +} + +// Declare globals provided by Django's JavaScript Catalog +// For more information, see: https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#module-django.views.i18n +declare global { + // Wagtail globals + + interface WagtailConfig { + ADMIN_API: { + PAGES: string; + DOCUMENTS: string; + IMAGES: string; + EXTRA_CHILDREN_PARAMETERS: string; + }; + + I18N_ENABLED: boolean; + LOCALES: { + code: string; + /* eslint-disable-next-line camelcase */ + display_name: string; + }[]; + } + + const wagtailConfig: WagtailConfig; + + // Django i18n utilities + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#gettext + function gettext(text: string): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#ngettext + function ngettext(singular: string, plural: string, count: number): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#interpolate + // FIXME export default function interpolate(...): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#get-format + type FormatType = + | 'DATE_FORMAT' + | 'DATE_INPUT_FORMATS' + | 'DATETIME_FORMAT' + | 'DATETIME_INPUT_FORMATS' + | 'DECIMAL_SEPARATOR' + | 'FIRST_DAY_OF_WEEK' + | 'MONTH_DAY_FORMAT' + | 'NUMBER_GROUPING' + | 'SHORT_DATE_FORMAT' + | 'SHORT_DATETIME_FORMAT' + | 'THOUSAND_SEPARATOR' + | 'TIME_FORMAT' + | 'TIME_INPUT_FORMATS' + | 'YEAR_MONTH_FORMAT'; + + function get_format(formatType: FormatType): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#gettext_noop + function gettext_noop(text: string): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#pgettext + function pgettext(context: string, text: string): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#npgettext + function pgettext(context: string, text: string, count: number): string; + + // https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#pluralidx + function pluralidx(count: number): boolean; +} diff --git a/wagtail_ab_testing/static_src/main.tsx b/wagtail_ab_testing/static_src/main.tsx new file mode 100644 index 0000000..e69de29 diff --git a/wagtail_ab_testing/test/__init__.py b/wagtail_ab_testing/test/__init__.py new file mode 100644 index 0000000..3d623a3 --- /dev/null +++ b/wagtail_ab_testing/test/__init__.py @@ -0,0 +1 @@ +default_app_config = "wagtail_ab_testing.test.apps.WagtailAbTestingTestAppConfig" diff --git a/wagtail_ab_testing/test/apps.py b/wagtail_ab_testing/test/apps.py new file mode 100644 index 0000000..1091d90 --- /dev/null +++ b/wagtail_ab_testing/test/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class WagtailAbTestingTestAppConfig(AppConfig): + label = "wagtail_ab_testing_test" + name = "wagtail_ab_testing.test" + verbose_name = "A/B Testing tests" diff --git a/wagtail_ab_testing/test/settings.py b/wagtail_ab_testing/test/settings.py new file mode 100644 index 0000000..7036de9 --- /dev/null +++ b/wagtail_ab_testing/test/settings.py @@ -0,0 +1,157 @@ +""" +Django settings for temp project. + +Generated by 'django-admin startproject' using Django 1.10.5. + +For more information on this file, see +https://docs.djangoproject.com/en/1.10/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.10/ref/settings/ +""" + +import os +import dj_database_url + +# Build paths inside the project like this: os.path.join(PROJECT_DIR, ...) +PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BASE_DIR = os.path.dirname(PROJECT_DIR) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "c6u0-9c!7nilj_ysatsda0(f@e_2mws2f!6m0n^o*4#*q#kzp)" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ["localhost", "testserver"] + + +# Application definition + +INSTALLED_APPS = [ + "wagtail_ab_testing", + "wagtail_ab_testing.test", + "wagtail.contrib.search_promotions", + "wagtail.contrib.forms", + "wagtail.contrib.redirects", + "wagtail.embeds", + "wagtail.users", + "wagtail.snippets", + "wagtail.documents", + "wagtail.images", + "wagtail.search", + "wagtail.admin", + "wagtail.api.v2", + "wagtail.contrib.modeladmin", + "wagtail.contrib.routable_page", + "wagtail.contrib.styleguide", + "wagtail.sites", + "wagtail.core", + "taggit", + "rest_framework", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.sitemaps", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "wagtail.contrib.redirects.middleware.RedirectMiddleware", +] + +ROOT_URLCONF = "wagtail_ab_testing.test.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ] + }, + } +] + + +# Using DatabaseCache to make sure that the cache is cleared between tests. +# This prevents false-positives in some wagtail core tests where we are +# changing the 'wagtail_root_paths' key which may cause future tests to fail. +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.db.DatabaseCache", + "LOCATION": "cache", + } +} + + +# don't use the intentionally slow default password hasher +PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",) + + +# Database +# https://docs.djangoproject.com/en/1.10/ref/settings/#databases + +DATABASES = { + "default": dj_database_url.config(default="sqlite:///test_wagtail_ab_testing.db"), +} + + +# Password validation +# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" + }, + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, + {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, + {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.10/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.8/howto/static-files/ + +STATICFILES_FINDERS = [ + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", +] + +STATICFILES_DIRS = [os.path.join(PROJECT_DIR, "static")] + +STATIC_ROOT = os.path.join(PROJECT_DIR, "collect_static") +STATIC_URL = "/static/" + +MEDIA_ROOT = os.path.join(PROJECT_DIR, "media") diff --git a/wagtail_ab_testing/test/tests/__init__.py b/wagtail_ab_testing/test/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wagtail_ab_testing/test/urls.py b/wagtail_ab_testing/test/urls.py new file mode 100644 index 0000000..ceabbf1 --- /dev/null +++ b/wagtail_ab_testing/test/urls.py @@ -0,0 +1,15 @@ +from django.conf.urls import include, url +from django.conf.urls.i18n import i18n_patterns +from django.contrib import admin + +from wagtail.admin import urls as wagtailadmin_urls +from wagtail.documents import urls as wagtaildocs_urls +from wagtail.core import urls as wagtail_urls + +urlpatterns = [ + url(r"^django-admin/", admin.site.urls), + url(r"^admin/", include(wagtailadmin_urls)), + url(r"^documents/", include(wagtaildocs_urls)), +] + +urlpatterns += i18n_patterns(url(r"", include(wagtail_urls))) diff --git a/wagtail_ab_testing/wagtail_hooks.py b/wagtail_ab_testing/wagtail_hooks.py new file mode 100644 index 0000000..d1886d0 --- /dev/null +++ b/wagtail_ab_testing/wagtail_hooks.py @@ -0,0 +1,24 @@ +from django.urls import path, include +from django.views.i18n import JavaScriptCatalog + +from wagtail.core import hooks + + +@hooks.register("register_admin_urls") +def register_admin_urls(): + urls = [ + path('jsi18n/', JavaScriptCatalog.as_view(packages=['wagtail_ab_testing']), name='javascript_catalog'), + + # Add your other URLs here, and they will appear under `/admin/ab_testing/` + # Note: you do not need to check for authentication in views added here, Wagtail does this for you! + ] + + return [ + path( + "ab_testing/", + include( + (urls, "wagtail_ab_testing"), + namespace="wagtail_ab_testing", + ), + ) + ] diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..abe17af --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,52 @@ +const path = require('path'); + +module.exports = { + entry: './wagtail_ab_testing/static_src/main.tsx', + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/ + }, + { + test: /\.scss$/, + use: [ + 'style-loader', + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.css$/, + use: [ + 'style-loader', + 'css-loader', + ] + }, + { + test: /\.svg$/, + use: ['@svgr/webpack'], + }, + { + test: /\.(png|jpg|gif)$/, + use: [ + 'file-loader' + ] + } + ] + }, + resolve: { + extensions: [ '.tsx', '.ts', '.js' ] + }, + externals: { + /* These are provided by Wagtail */ + 'react': 'React', + 'react-dom': 'ReactDOM', + 'gettext': 'gettext', + }, + output: { + path: path.resolve(__dirname, 'wagtail_ab_testing/static/wagtail_ab_testing/js'), + filename: 'wagtail-ab-testing.js' + } +};