{"id":19634850,"url":"https://github.com/wagtail-nest/wagtail-ab-testing","last_synced_at":"2025-04-06T10:11:52.948Z","repository":{"id":38994459,"uuid":"309701708","full_name":"wagtail-nest/wagtail-ab-testing","owner":"wagtail-nest","description":"A/B testing for Wagtail","archived":false,"fork":false,"pushed_at":"2025-03-17T15:06:12.000Z","size":2420,"stargazers_count":22,"open_issues_count":10,"forks_count":21,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-30T08:12:08.109Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wagtail-nest.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-03T13:53:42.000Z","updated_at":"2025-03-28T13:43:26.000Z","dependencies_parsed_at":"2023-11-15T10:32:57.369Z","dependency_job_id":"5be4cfa8-5d60-4b0a-85b1-ccbea3be21dc","html_url":"https://github.com/wagtail-nest/wagtail-ab-testing","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagtail-nest%2Fwagtail-ab-testing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagtail-nest%2Fwagtail-ab-testing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagtail-nest%2Fwagtail-ab-testing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagtail-nest%2Fwagtail-ab-testing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wagtail-nest","download_url":"https://codeload.github.com/wagtail-nest/wagtail-ab-testing/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247464222,"owners_count":20942970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T12:22:54.363Z","updated_at":"2025-04-06T10:11:52.925Z","avatar_url":"https://github.com/wagtail-nest.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Wagtail A/B Testing\n\n[![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD-blue.svg?style=flat)](https://opensource.org/licenses/BSD-3-Clause)\n[![Build status](https://github.com/wagtail-nest/wagtail-ab-testing/actions/workflows/test.yml/badge.svg)](https://github.com/wagtail-nest/wagtail-ab-testing/actions/workflows/test.yml)\n[![codecov](https://img.shields.io/codecov/c/github/wagtail-nest/wagtail-ab-testing?style=flat)](https://codecov.io/gh/wagtail-nest/wagtail-ab-testing)\n[![Version](https://img.shields.io/pypi/v/wagtail-ab-testing.svg?style=flat)](https://pypi.python.org/pypi/wagtail-ab-testing/)\n[![Monthly downloads](https://img.shields.io/pypi/dm/wagtail-ab-testing.svg?logo=Downloads)](https://pypi.python.org/pypi/wagtail-ab-testing/)\n\nWagtail A/B Testing is an A/B testing package for Wagtail that allows users to create and manage A/B tests on pages through the Wagtail admin.\n\nKey features:\n\n-   Create an A/B test on any page from within Wagtail\n-   Tests using page revisions (no need to create separate pages for the variants)\n-   It prevents users from editing the page while a test is in progress\n-   Calculates confidence using a Pearson's chi-squared test\n\n[Changelog](https://github.com/torchbox/wagtail-ab-testing/blob/main/CHANGELOG.md)\n\n## Usage\n\nWagtail A/B Testing works with Django 4.2+, Wagtail 5.2+ on Python 3.9+ environments.\n\n### Creating an A/B test\n\nAny user with the \"Create A/B test\" permission can create an A/B test by clicking \"Save and create A/B test\" from the page's action menu.\n\nThe first page shows the user the difference between the content in the latest draft against the live version of the page.\nThis allows them to check what changes on the page are going to be tested.\n\nOnce they've confirmed that, the user is taken to a form to insert the test name/hypothesis, select a goal, and sample size.\n\n![Screenshot of Wagtail A/B Testing create page](/screenshot-create.png)\n\n### Monitoring test progress\n\nWhile the test is running, the page's edit view gets replaced with a dashboard showing the current test progress.\nUsers cannot edit the page until the test is completed or cancelled.\n\nAny user with permission to publish the page can start, pause, resume or end A/B tests on that page.\n\n![Screenshot of Wagtail A/B Testing](/screenshot.png)\n\n### Finishing the test\n\nThe test stops automatically when the number of participants reaches the sample size.\nBased on the results shown, a user must decide whether to publish the new changes or revert to the old version of the page.\n\nOnce they've chosen, the page edit view returns to normal.\nThe results from this A/B test remain accessible under the A/B testing tab or from the A/B testing report.\n\n![Screenshot of Wagtail A/B Testing](/screenshot-finish.png)\n\n## Installation\n\nFirstly, install the `wagtail-ab-testing` package from PyPI:\n\n    pip install wagtail-ab-testing\n\nThen add it into `INSTALLED_APPS`:\n\n```python\nINSTALLED_APPS = [\n    # ...\n    'wagtail_ab_testing',\n    # ...\n]\n```\n\nThen add the following to your URLconf:\n\n```python\nfrom wagtail_ab_testing import urls as ab_testing_urls\n\nurlpatterns = [\n    ...\n\n    path('abtesting/', include(ab_testing_urls)),\n]\n```\n\nFinally, add the tracking script to your base HTML template:\n\n```django+HTML\n{# Insert this at the top of the template #}\n{% load wagtail_ab_testing_tags %}\n\n...\n\n{# Insert this where you would normally insert a \u003cscript\u003e tag #}\n{% wagtail_ab_testing_script %}\n```\n\n## Implementing custom goal event types\n\nOut of the box, Wagtail A/B testing provides a \"Visit page\" goal event type which you can use to track when users visit a goal page.\nIt also supports custom goal types, which can be used for tracking other events such as making a purchase, submitting a form, or clicking a link.\n\nTo implement a custom goal event type, firstly register your type using the `register_ab_testing_event_types` hook, this would\nadd your goal type to the list of options shown to users when they create A/B tests:\n\n```python\n# myapp/wagtail_hooks.py\n\nfrom wagtail import hooks\nfrom wagtail_ab_testing.events import BaseEvent\n\n\nclass CustomEvent(BaseEvent):\n    name = \"Name of the event type\"\n    requires_page = True  # Set to False to create a \"Global\" event type that could be reached on any page\n\n    def get_page_types(self):\n        return [\n            # Return a list of page models that can be used as destination pages for this event type\n            # For example, if this 'event type' is for a 'call to action' button that only appears on\n            # the homepage, put your `HomePage` model here.\n        ]\n\n\n@hooks.register('register_ab_testing_event_types')\ndef register_submit_form_event_type():\n    return {\n        'slug-of-the-event-type': CustomEvent,\n    }\n\n```\n\nNext, you need to tell Wagtail A/B testing whenever a user triggers the goal. This can be done by calling `wagtailAbTesting.triggerEvent()`\nin the browser:\n\n```javascript\nif (window.wagtailAbTesting) {\n    wagtailAbTesting.triggerEvent('slug-of-the-event-type');\n}\n```\n\nThe JavaScript library tracks A/B tests using `localStorage`, so this will only call the server if the user is participating in an A/B test with the provided goal type and the current page is the goal page.\n\n#### Example: Adding a \"Submit form\" event type\n\nWe will add a \"Submit form\" event type for a `ContactUsFormPage` page type in this example.\n\nFirstly, we need to register the event type. To do this, implement a handler for the `register_ab_testing_event_types` hook in your app:\n\n```python\n# myapp/wagtail_hooks.py\n\nfrom wagtail import hooks\nfrom wagtail_ab_testing.events import BaseEvent\n\nfrom .models import ContactUsFormPage\n\n\nclass SubmitFormPageEvent(BaseEvent):\n    name = \"Submit form page\"\n\n    def get_page_types(self):\n        # Only allow this event type to be used if he user has\n        # selected an instance of `ContactUsFormPage` as the goal\n        return [\n            ContactUsFormPage,\n        ]\n\n\n@hooks.register('register_ab_testing_event_types')\ndef register_submit_form_event_type():\n    return {\n        'submit-contact-us-form': SubmitFormPageEvent,\n    }\n```\n\nNext, we need to add some code to the frontend to trigger this event whenever a user submits the form:\n\n```django+HTML\n# templates/forms/contact_us_form_page.html\n\n\u003cform id=\"form\"\u003e\n    ...\n\u003c/form\u003e\n\n\u003cscript\u003e\n    if (window.wagtailAbTesting) {\n        document.getElementById('form').addEventListener('submit', function() {\n            wagtailAbTesting.triggerEvent('submit-contact-us-form');\n        });\n    }\n\u003c/script\u003e\n```\n\n## Running A/B tests on a site that uses Cloudflare caching\n\nTo run Wagtail A/B testing on a site that uses Cloudflare, firstly generate a secure random string to use as a token, and configure that token in your Django settings file:\n\n```python\nWAGTAIL_AB_TESTING_WORKER_TOKEN = '\u003ctoken here\u003e'\n```\n\nThen set up a Cloudflare Worker based on the following JavaScript:\n\n```javascript\n// Set to false if Cloudflare shouldn't automatically redirect requests to use HTTPS\nconst ENFORCE_HTTPS = true;\n\nexport default {\n    async fetch(request, env, ctx) {\n        const url = new URL(request.url);\n\n        // Set this to the domain name of your backend server\n        const WAGTAIL_DOMAIN = env.WAGTAIL_DOMAIN;\n\n        // This should match the token on your Django settings\n        const WAGTAIL_AB_TESTING_WORKER_TOKEN =\n            env.WAGTAIL_AB_TESTING_WORKER_TOKEN;\n\n        if (url.protocol == 'http:' \u0026\u0026 ENFORCE_HTTPS) {\n            url.protocol = 'https:';\n            return Response.redirect(url, 301);\n        }\n\n        if (request.method === 'GET') {\n            const newRequest = new Request(request, {\n                headers: {\n                    ...request.headers,\n                    Authorization: 'Token ' + WAGTAIL_AB_TESTING_WORKER_TOKEN,\n                    'X-Requested-With': 'WagtailAbTestingWorker',\n                },\n            });\n\n            url.hostname = WAGTAIL_DOMAIN;\n            response = await fetch(url.toString(), newRequest);\n\n            // If there is a test running at the URL, the worker would return\n            // a JSON response containing both versions of the page. Also, it\n            // returns the test ID in the X-WagtailAbTesting-Test header.\n            const testId = response.headers.get('X-WagtailAbTesting-Test');\n            if (testId) {\n                // Participants of a test would have a cookie that tells us which\n                // version of the page being tested on that they should see\n                // If they don't have this cookie, serve a random version\n                const versionCookieName = `abtesting-${testId}-version`;\n                const cookie = request.headers.get('cookie');\n                let version;\n                if (cookie \u0026\u0026 cookie.includes(`${versionCookieName}=control`)) {\n                    version = 'control';\n                } else if (\n                    cookie \u0026\u0026\n                    cookie.includes(`${versionCookieName}=variant`)\n                ) {\n                    version = 'variant';\n                } else if (Math.random() \u003c 0.5) {\n                    version = 'control';\n                } else {\n                    version = 'variant';\n                }\n\n                return response.json().then((json) =\u003e {\n                    return new Response(json[version], {\n                        headers: {\n                            ...response.headers,\n                            'Content-Type': 'text/html',\n                        },\n                    });\n                });\n            }\n\n            return response;\n        } else {\n            return await fetch(url.toString(), request);\n        }\n    },\n};\n```\n\nYou can use CloudFlare's `wrangler` to setup your worker. On an empty directory, install `wrangler`:\n\n```sh\nnpm install wrangler --save-dev\n```\n\nand then initialise a new Wrangler project:\n\n```sh\nnpx wrangler init\n```\n\nFollow the CLI prompt until it generates a project for you, then add the JS script above to `src/index.js`.\n\nAdd a `WAGTAIL_AB_TESTING_WORKER_TOKEN` variable to the worker, giving it the same token value that you generated earlier. Make sure to also setup a `WAGTAIL_DOMAIN` variable with the value of the domain where your website is hosted (e.g. `\"www.mysite.com\"`).\n\nFinally, add a route into Cloudflare so that it routes all traffic through this worker.\n\n## Contribution\n\n### Install\n\nTo make changes to this project, first fork this repository and clone it to your local system:\n\n```shell\ngit clone link-to-your-forked-repo\ncd wagtail-ab-testing\n```\n\nWith your preferred virtualenv activated, install testing dependencies:\n\n```shell\npython -m pip install -e .[testing]\n```\n\n### How to run tests\n\n```shell\npython testmanage.py test\n```\n\n### Formatting and linting\n\nWe are using `pre-commit` to ensure that all code is formatted and linted before committing. To install the pre-commit hooks, run:\n\n```shell\npre-commit install\n```\n\nThe pre-commit hooks will run automatically before each commit. Or you can run them manually with:\n\n```shell\npre-commit run --all-files\n```\n\n## Credits\n\n`wagtail-ab-testing` was originally created by [Karl Hobley](https://github.com/kaedroho)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwagtail-nest%2Fwagtail-ab-testing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwagtail-nest%2Fwagtail-ab-testing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwagtail-nest%2Fwagtail-ab-testing/lists"}