diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 43d50a71..00000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-env node */ -module.exports = { - root: true, - extends: [ - 'plugin:vue/vue3-essential', - 'eslint:recommended', - '@vue/eslint-config-typescript/recommended', - ], - env: { - 'vue/setup-compiler-macros': true, - }, - rules: { - '@typescript-eslint/no-unused-vars': [ - 'error', - {argsIgnorePattern: '^_'}, - ], - '@typescript-eslint/no-empty-function': [ - 'error', - {allow: ['methods']}, - ], - 'max-len': ['error', {code: 80, comments: 80}], - 'operator-linebreak': [ - 'error', - 'before', - {overrides: {'=': 'after'}}, - ], - 'vue/multi-word-component-names': 'off', - // For the Paramable interface, v-model directives need type annotation - 'vue/valid-v-model': 'off', - }, -} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7dcf091e..07be448f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,4 +1,10 @@ -By submitting this PR, I am indicating to the Numberscope maintainers that I have read and understood the [contributing guidelines](https://numberscope.colorado.edu/doc/CONTRIBUTING/) and that this PR follows those guidelines to the best of my knowledge. I have also read the [pull request checklist](https://numberscope.colorado.edu/doc/doc/pull-request-checklist/) and followed the instructions therein. +By submitting this PR, I am indicating to the Numberscope maintainers that I +have read and understood the +[contributing guidelines](https://numberscope.colorado.edu/doc/CONTRIBUTING/) +and that this PR follows those guidelines to the best of my knowledge. I have +also read the +[pull request checklist](https://numberscope.colorado.edu/doc/doc/pull-request-checklist/) +and followed the instructions therein.
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4e717ab9..1e773c27 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,5 +1,10 @@ name: 'CI' -on: pull_request +on: + push: + branches: + - main + pull_request: + jobs: lint: runs-on: ubuntu-22.04 @@ -12,13 +17,15 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v3 with: - node-version: '18.x' + node-version: '20.x' - name: Install dependencies run: npm install - name: Lint run: npm run lint:check + - name: Type check + run: npm run typecheck test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python @@ -28,8 +35,23 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v3 with: - node-version: '18.x' + node-version: '20.x' + - name: Install d2 document generator + run: curl -fsSL https://d2lang.com/install.sh | sh -s -- - name: Install dependencies run: npm install - - name: Test + - name: Unit test run: npm run test:unit + - name: Build production site + run: npm run build + - name: Install Playwright browsers + run: npx playwright install --with-deps + - name: End-to-end test + run: npx playwright test -c e2e/playci.config.ts +### Uncomment when you need to re-extract snapshots +# - name: Extract snapshots +# if: always() +# uses: actions/upload-artifact@v4 +# with: +# name: ci_actual_snapshots +# path: e2e/results/manual/output diff --git a/.gitignore b/.gitignore index 9e9b3eab..7566928d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,14 +4,25 @@ logs npm-debug.log* # Subdirectories generated by various tools +.cache .venv coverage -dist node_modules -site # File for storing your local configuration *.local # Emacs backup files *~ + +# Build files +dist +etc/npm_install_ran_at +etc/typechecked_at +site + +# End-to-end testing +e2e/.last_cl_args +e2e/certificate +e2e/docker_image_built +e2e/results diff --git a/.husky/pre-commit b/.husky/pre-commit index 91864d2b..07cf0b26 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,8 +1,8 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npm run lint:list || echo 'Files listed above will be reformatted and staged.' -npm run lint:staged +node tools/cleancommit.js npm run lint:check -npm run typecheck npm run test:unit +npm run test:e2e + diff --git a/.prettierignore b/.prettierignore index 33c59672..da926c4d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,20 +2,24 @@ *.ico *.jpg *.png +*.svg *.ttf *.txt *.generator *.local *~ +.env +.env.* +.github/workflows .gitignore .husky .prettierignore .vscode coverage dist +index.html node_modules package-lock.json -index.html +e2e/*-snapshots +e2e/results site -.env -.env.* diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 7d6cf731..00000000 --- a/.prettierrc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "arrowParens": "avoid", - "bracketSameLine": true, - "bracketSpacing": false, - "embeddedLanguageFormatting": "auto", - "htmlWhitespaceSensitivity": "css", - "insertPragma": false, - "jsxSingleQuote": false, - "printWidth": 78, - "proseWrap": "always", - "quoteProps": "as-needed", - "requirePragma": false, - "semi": false, - "singleQuote": true, - "tabWidth": 4, - "trailingComma": "es5", - "useTabs": false, - "vueIndentScriptAndStyle": true -} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 95472f8f..c68c7d84 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,45 +1,40 @@ # Contributing to Numberscope -## If you are new to software development and like written guides... +## If you are new to software development... Read our [onboarding doc](./doc/onboarding.md). -## If you are new to software development and like videos... - -Watch the video series on contributing to Numberscope -[here](https://www.youtube.com/playlist?list=PLA4KIQBQQRb5ccOdr9v0iLw_fKHup1PkU). -This video series will introduce you to Numberscope, GitHub, the software we -use in the Numberscope project, and the workflow for contributing to -Numberscope. - ## If you are experienced, follow these steps... (This assumes you're working on the numberscope/frontscope repository, but it -applies to other repos as well.) - -1. [Create a fork of the numberscope/frontscope repo](./doc/working-with-git-and-github.md#create-a-fork). -2. [Clone your fork of numberscope/frontscope](./doc/working-with-git-and-github.md#clone-a-repo). -3. [Create a branch for your contribution](./doc/working-with-git-and-github.md#create-a-branch). -4. If you are unfamiliar with them or would like a refresher, - [read about basic Git operations](./doc/working-with-git-and-github.md#basic-operations). -5. [Push your branch to GitHub](./doc/working-with-git-and-github.md#push-a-branch). -6. [Read Numberscope's coding principles guide](./doc/code-principles.md). -7. [Read Numberscope's style guide](./doc/code-style.md). You should also - familiarize yourself with frontscope's internal code structure and APIs. - Documentation on these topics is generally incorporated into the relevant - source files (see [this example](src/shared/math.ts)). However, it is all - gathered in the "Internal code and APIs" section of the navigation bar in - the [online docs](https://numberscope.colorado.edu/doc). -8. If you are working on a visualizer, read - [the doc on making a visualizer](./doc/making-a-visualizer.md). -9. [Work through Numberscope's pull request checklist](./doc/pull-request-checklist.md). -10. [Submit a pull request](./doc/working-with-git-and-github.md#submit-a-pull-request). +applies to other repos as well. If you are unfamiliear with basic git +operations or would like a refresher, we +[have a guide](doc/working-with-git-and-github.md#basic-operations).) + +1. [Clone the numberscope/frontscope repository](doc/working-with-git-and-github.md#clone-a-repo). +2. [Create a branch for your contribution](doc/working-with-git-and-github.md#create-a-branch). +3. [Read Numberscope's coding principles guide](doc/code-principles.md). +4. Familiarize yourself with frontscope's + [code organization](doc/code-organization.md) and internal APIs. Some + information on the latter topic may only be found in comments in the + relevant sources files, as the documentation project is ongoing. However, + all such formal documentation currently being generated is gathered in the + "Internal code and APIs" section of the navigation bar in the + [online docs](https://numberscope.colorado.edu/doc). +5. If you are working on a visualizer, read the documentation section on + [making a visualizer](doc/visualizer-overview.md). +6. Implement your changes. +7. [Create a fork of the numberscope/frontscope repo](doc/working-with-git-and-github.md#create-a-fork). +8. [Add your fork as a remote](doc/working-with-git-and-github.md#add-a-remote). +9. [Push your branch to GitHub](doc/working-with-git-and-github.md#push-a-branch). +10. [Work through Numberscope's pull request checklist](doc/pull-request-checklist.md). +11. [Submit a pull request](doc/working-with-git-and-github.md#submit-a-pull-request). ## If you need help with Git and contributing... If you have a Git setup that isn't what Numberscope requires (i.e. do your work in your own fork on a feature branch) check out our doc on -[Gitting it right](./doc/gitting-it-right.md). +[Gitting it right](doc/gitting-it-right.md). ## A note on code organization @@ -64,24 +59,25 @@ their factorizations, see `backscope` applies to other repos as well.) If you've read the -[Numberscope code principles guide](./doc/code-principles.md) and the -[Numberscope code style guide](./doc/code-style.md), and you think your code -is ready to be reviewed by someone at Numberscope, follow these steps: +[Numberscope code principles guide](./doc/code-principles.md) and have adhered +to the [code organization](./doc/code-organization.md), and you think your +code is ready to be reviewed by someone at Numberscope, follow these steps: 1. Work through [Numberscope's pull request checklist](./doc/pull-request-checklist.md). -2. Sync your fork with the main numberscope/frontscope repository. The easiest - way to do this is to navigate to the page of your fork of - numberscope/frontscope and click "Sync fork" (see the picture below). - ![A screenshot of the Sync fork option](./doc/img/sync-fork.png) Another - way to do this is to - [add numberscope/frontscope as a remote](./doc/working-with-git-and-github.md#add-a-remote) - and - [sync your fork with the remote original](./doc/working-with-git-and-github.md#sync-local-fork-with-remote-original). +2. Make sure your branch is based on the latest version of the `main` branch + from the official repository. The simplest way to do this is to + [sync your local clone](doc/working-with-git-and-github.md#sync-a-local-clone) + and if your copy of `main` pulled additional new commits in that process + (as opposed to being reported as "already up to date"), then go ahead and + [rebase your branch](doc/working-with-git-and-github.md#rebase-your-branch). 3. Navigate to the numberscope/frontscope repository. If your fork is synced up with the main numberscope/frontscope repository correctly, you should see a button (see the image below) that says "Compare & pull request". ![A screenshot of the Compare & pull request -button](./doc/img/compare-and-pull-request.png) +button](doc/img/compare-and-pull-request.png) Click that button, write up some notes for your pull request, and click the - "Create pull request button". + "Create pull request button". Our + [Working with Git guide](doc/working-with-git-and-github.md) has more + details about + [submitting a pull request](doc/working-with-git-and-github.md#submit-a-pull-request). diff --git a/doc/about.md b/doc/about.md index d54c6e0c..c288630d 100644 --- a/doc/about.md +++ b/doc/about.md @@ -18,23 +18,28 @@ You can find [Numberscope on GitHub](https://github.com/numberscope) or ## Contributors -- Abdullatif Khalid Abduljaleel -- Tobias Aldape -- Khaled Allen -- Isabel Anaya -- Sebastian Bozlee -- Olivia Brobin -- Devlin Costello -- Aaron Fenyes -- Brendan Heaney -- Steven Hristopoulos -- Clyde Kertzer -- Jennifer Leong -- Ang Li -- Theodore Lincke -- Josiah Martinez -- Willem Mirkovich -- Liam Mulhall -- Daniel H. Taylor + ++---------------------------------+-------------------------------+ +| • Abdullatif Khalid Abduljaleel | • Steven Hristopoulos | ++---------------------------------+-------------------------------+ +| • Tobias Aldape | • Clyde Kertzer | ++---------------------------------+-------------------------------+ +| • Khaled Allen | • Jennifer Leong | ++---------------------------------+-------------------------------+ +| • Isabel Anaya | • Ang Li | ++---------------------------------+-------------------------------+ +| • Sebastian Bozlee | • Theodore Lincke | ++---------------------------------+-------------------------------+ +| • Olivia Brobin | • Josiah Martinez | ++---------------------------------+-------------------------------+ +| • Devlin Costello | • Willem Mirkovich | ++---------------------------------+-------------------------------+ +| • Aaron Fenyes | • Liam Mulhall | ++---------------------------------+-------------------------------+ +| • Brendan Heaney | • Daniel H. Taylor | ++---------------------------------+-------------------------------+ +| • Delft CSE2000 Spring '24 Team: Matej Bavec, Ziggy Beijer,
| +|    Kaldis Bērziņš, Max Derbenwick, and Gido Vitner | ++-----------------------------------------------------------------+ [Acknowledgments](acknowledgments.md) diff --git a/doc/behind-the-scenes.md b/doc/behind-the-scenes.md deleted file mode 100644 index d6e1926a..00000000 --- a/doc/behind-the-scenes.md +++ /dev/null @@ -1,82 +0,0 @@ -# Visualizers: Behind the Scenes - - - -In the guide to [making a visualizer](making-a-visualizer.md), we saw how to -extend the -[`P5Visualizer`](making-a-visualizer.md#making-a-p5-visualizer-in-detail) base -class. Now, let's take a peek at how a base class works internally. This page -will be most useful to you if you want to write a new base class, or to build -a visualizer so different from anything else that it shouldn't have a base -class. However, you can also use this knowledge to override the default -behavior of a base class you're extending. By overriding methods like -`inhabit()`, `show()`, `stop()`, and `depart()`, you can customize your -visualizer's behavior more deeply than usual. - -Behind the scenes, a visualizer base class is an implementation of the -[visualizer interface](#the-visualizer-interface). To support parameters, the -base class also has to implement the -[parameterizable object interface](#the-parameterizable-object-interface). To -write a new base class, or to build a visualizer without one, you'll have to -implement these interfaces yourself. That means including all the required -properties and methods, and making sure they behave in the way the engine -expects. - -## Visualizers - -Source: `VisualizerInterface.ts`. - -### The visualizer interface - -| Method | Required behavior | -| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `visualization()` | Returns a string saying what type of visualizer this is. Typically, this would depend only on the class of the visualizer, rather than varying from instance to instance. | -| `view(seq)` | Load the given sequence into the visualizer, so that the drawing operations in later function calls will be able to access it. This method should not do any drawing. | -| `inhabit(element)` | Insert a view of the the visualizer into the given `HTMLElement`. This element is typically a `div` whose size is already set up to comprise the available space for visualization. The `inhabit()` method should not do any drawing. | -| `show()` | Start or resume display of the visualization. When this is called, you can (and should!) actually start drawing things. | -| `stop()` | Pause display of the visualization (i.e., don't do any more drawing until another call to show). Don't erase any visualization produced so far or otherwise clean up the visualizer. | -| `depart()` | Throw out the visualization, release its resources, remove its injected DOM elements, and do any other required cleanup. After this call, the visualizer must support `inhabit()` being called again, perhaps with a different div, to re-initialize the visualization. | - -## Parameterizable objects - -Source: `Paramable.ts`. - -### The parameterizable object interface - -An object that takes parameters, like a visualizer or a sequence, has to -implement `ParamableInterface`. An easy way to do this is to extend the -generic implementation, [`Paramable`](#the-paramable-base-class). - -| Property | Description | -| ------------- | ------------------------------------------------------- | -| `name` | See [generic implementation](#the-paramable-base-class) | -| `description` | See [generic implementation](#the-paramable-base-class) | - -| Method | Required behavior | -| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `isValid` | A boolean that says whether the current configuration of parameter values is self-consistent and safe to use. Generally, this will be set automatically based on the output of `checkParameters()`, which is mentioned in the description of `validate()` below. | -| `params` | A parameterizable object has to come with a set of user-facing parameters—even if the set is empty. The `params` property is an object mapping parameter names to parameter objects—that is, (plain) objects implementing the parameter interface (`ParamInterface`). A parameter object describes how a parameter should appear in the UI, what kind of values it can take, whether it's required, and so on. | -| `validate()` | Check whether the current configuration of parameter values is valid, and call the `assignParameters()` method below if so. Return a `ValidationStatus` object that tells the engine whether the check and assignment succeeded. Whenever the engine wants to load a parameter configuration into a parameterizable object, it will use `validate()` to do so. If it gets back a `ValidationStatus` with `isValid = true`, it will proceed with whatever it is doing. If it gets a status with `isValid = false`, it will will stop what it is doing and help the user fix the problem by displaying any error messages the status carries. Most parameterizable objects will extend [`Paramable`](#the-paramable-base-class) and use its default implementation of `validate()`. | -| `assignParameters()` | Copy the `value` property of each item in `params` to the place where the implementing object will access it. Typically, that means copying to top-level properties of the object. The implementing object should only use parameter values supplied by `assignParameters()`, because these have been vetted by `validate()`. In contrast, if you took values directly from `params` they could be unvalidated, and they can change from valid to invalid at any time. | -| `refreshParams()` | Copy the current working values of the parameters back into the `value` properties in the `params` object, so they can be reflected in the parameter UI. This method is used by objects that can update their own parameters, rather than just having parameters assigned through the standard parameter UI. | - -### The paramable base class - -The `Paramable` class is a generic implementation of the parameterizable -object interface. - -| Parameter | Value | -| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | `'Paramable'` | -| `description` | `'A class which can have parameters set'` Derived classes may override this property, but it's currently not displayed anywhere user-visible. | - -| Method | Implementation | -| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `validate()` | Checks parameter validity by calling the `checkParameters()` method, and then handles all the other responsibilities of `validate()`. A class extending `Paramable` will typically just override `checkParameters()`, which returns a `ValidationStatus` that says whether the current parameter configuration is self-consistent and safe to use. | diff --git a/doc/code-organization.md b/doc/code-organization.md new file mode 100644 index 00000000..5bb4d109 --- /dev/null +++ b/doc/code-organization.md @@ -0,0 +1,84 @@ +# Code organization + +Here's a brief overview of the directory layout of this project, to help you +find what you're looking for, and to guide you when you need to create new +files. + +## Top level of project + +Here you will find the obligatory README.md and licensing/contributing +information, project manifests like package.json and requirements.txt, and the +top-level index.html for vite. There are also a few hidden (dotfile) +configuration files, like git's own .git and .gitignore files, similar ones +for github and Husky, and the .env files that control environment variables. +Otherwise, we try to keep the top level uncluttered, and you should rarely +need to create a file here. + +- ### etc/ -- Configuration files + + In particular, since frontscope uses so many tools, we have moved all of + their configuration files into etc/, as they were accumulating in the top + level. You should be able to find almost all configuration information + here, and any new tool that needs to be introduced should be invoked so as + to find its configuration file in the etc/ directory. As one example, the + Vite and Vitest configuration files are located here. As an exception, the + several tsconfig files for the TypeScript compiler are located in the + top-level directory itself -- we have not been able to get them to work in + any other location. + +- ### src/ -- Source code + + All of the actual TypeScript and Vue source code for the frontscope web + application is located here, along with assets that are used in-browser. + Only the top-level App.vue and main.ts are directly at the top level. The + rest are arranged in subdirectories: + + - #### src/assets/ and src/public/ + + Non-code assets needed when the site is deployed. + + - #### src/components/ -- Vue components + + - #### src/router/ -- Vue router + + - #### src/sequences/ -- Sequence implementations + + These classes provide the integer sequences that can be analyzed with + Numberscope. + + - #### src/shared/ + + Code needed by multiple other directories is located here. + + - #### src/views/ -- Vue views selected by the router + + - #### visualizers/ and visualizers-workbench/ + + These directories contain the implementations of the various means of + turning a sequence of numbers into a graphical presentation. In some + sense, these are the heart of Numberscope. The workbench directory is + for Visualizers under development (together with a template + permanently kept there), and those in visualizers/ will automatically + appear in the user interface + +- ### doc/ -- Documentation + + This directory contains the bulk of the content for the integrated + documentation site. Some content is also extracted from the source code + and incorporated when the documentation site is built. The styling + information for the site is in etc/doc_theme/. + +- ### e2e/ -- End-to-end in-browser testing + + All files relating to end-to-end testing are located here. Configuration + files are directly in this folder (another minor exception to the rule + mentioned above that all configuration is in etc/). + + - #### e2e/tests/ -- End-to-end test specifications + - #### e2e/results/ -- Files generated via testing. + +- ### tools/ -- Internal utilities + + Contains code for any custom utilities used for example in the building + and maintenance of frontscope. Nothing here should actually be needed by + the eventual web application. diff --git a/doc/code-principles.md b/doc/code-principles.md index 84ca59c7..d4539a33 100644 --- a/doc/code-principles.md +++ b/doc/code-principles.md @@ -28,3 +28,20 @@ Practically speaking this means: comments that merely repeat what the code already says - only have code that actually does something -- don't include any commented out code or code that can't be reached or run +- minimize the number of return statements in a function + - if there are conditions under which you can bail out easily, it's OK + to check for them near the top of the function and return from there + - otherwise, strive to have all returns at or near the end of the + function + +## Code style/formatting + +We rely on automated tools (ESLint and Prettier as of this writing) to keep +the layout/style of our code uniform for readability. Any new code will be +required to conform in the commit/PR process. In fact, often the tools will be +able to adjust the layout of the code for you: if a commit is rejected because +of formatting non-compliance, try `npm run lint` -- just be aware that it will +modify your source files. If you just want to see the issues without touching +any of your files, use `npm run lint:check`. See +[working with the package manager](working-with-pm.md) for details on these +and other scripts you can run. diff --git a/doc/code-style.md b/doc/code-style.md deleted file mode 100644 index 3ba96a3c..00000000 --- a/doc/code-style.md +++ /dev/null @@ -1,13 +0,0 @@ -# Code style - -Outside of the code formatting that is covered by our automated tools (ESLint -and Prettier as of this writing), we also like to: - -# Minimize the number of return statements in a function - -- If you can easily figure out that you need to bail out of a function, go - ahead and put a return at the top of the function and then proceed with - the rest of the function. -- Otherwise, try to minimize the number of return statements in a function. - Having them strewn throughout a function (especially a long function) is - generally hard to read. diff --git a/doc/code-tests.md b/doc/code-tests.md new file mode 100644 index 00000000..e62ad868 --- /dev/null +++ b/doc/code-tests.md @@ -0,0 +1,285 @@ +# Code tests + +For its front end, Numberscope uses [`vitest`](https://vitest.dev/) for unit +tests and low-level integration tests that run without a browser, and +[`Playwright`](https://playwright.dev/) for higher-level integration and +end-to-end integration tests that run in a browser. + +### Running tests + +The package manager is set up with scripts that make basic running of all +tests very convenient. To run all unit (vitest) tests, execute +`npm run test:unit`. To run all end-to-end (Playwright) tests, use +`npm run test:e2e`. + +Here is some more detailed information about running the end-to-end tests: + +- They result in accessing a considerable amount of (sequence) data over the + internet. Therefore, if you run them when your internet connection is poor + or inactive, some of the tests will fail. + +- Because they are rather expensive (in terms of computation time), the + end-to-end tests are designed to run only when something has changed since + they last passed. So if you run the tests, and they all pass, and you + immediately re-execute `npm run test:e2e`, it will simply tell you that + the tests are "up to date." + +- When all of the end-to-end tests have passed, the last three lines of the + output should typically look as follows: (There are two copies of the + "status": "passed" line because there are two batches of tests.) + +```txt + "status": "passed", + "status": "passed", +echo 'All tests passed' > e2e/certificate +``` + +- By default, the end-to-end tests mostly run in a + [Docker](https://www.docker.com/) container, which we have found maximizes + the reproducibility of the tests. So as explained in some more detail in + the [onboarding](./onboarding.md) page, you will need to install Docker on + your system and make sure the Docker daemon is running in order to run the + tests. + + You can also run them directly in your ambient system with + `npm run build && npm run test:e2e:cmd` or just `npm run test:e2e:ui` for + the graphical front end to Playwright, but note that some tests, + especially image comparison tests, may fail when in fact they would + succeed when run in the standard way. + +#### Customizing vitest runs + +If some situation in the code has led to one of the unit tests failing, you +may find yourself in a situation in which you need to run that test +repeatedly, and would rather not run all of the other tests. Fortunately, the +tests are organized by source directory. Hence, if the failure is occurring in +a test associated with the `src/shared` directory (say), then you can run just +the tests associated with that directory via `npm run test:unit src/shared`. +For a brief overview of other options you might use with running unit tests, +try `npm run test:unit -- --help`. (Note the extra `--` stuck in there, which +is needed because the test option you want to use here itself begins with +`--`.) + +#### Customizing Playwright runs + +You may find yourself in a similar situation with end-to-end testing. In this +case, all of the tests are specified in the top-level `e2e` directory. +However, they are organized into thematic files, which you can see via +`ls e2e`, for example. You can run just one of these collections of tests, say +the ones for the Gallery view in `e2e/gallery.spec.ts`, by executing +`npm run test:e2e gallery`. And similarly to unit tests, you can obtain an +overview of other possibilities via `npm run test:e2e -- --help`. + +## Creating tests + +If you are considering contributing code to Numberscope, you will very likely +need to create tests. The [pull request checklist](pull-request-checklist.md) +notes that any new feature (including a new visualizer) requires some test +that exercises it; and any bug fix should include a test that would have +failed with the buggy code but which now succeeds on the improved code. There +are somewhat different processes for adding each of the two kinds of tests. + +### Adding a unit test + +As mentioned above, these tests are organized per source directory, in +subdirectories named `__tests__/`. More than likely such a directory will +already exist alongside the source file you want to test, but if not, simply +create one -- vitest will find it automatically. Within the `__tests__/` +directory, the tests are organized by source file, and named accordingly, with +file extension `.spec.ts`. Again, if you are adding tests related to a file +that already has associated tests, you can just insert new tests in an +existing file. If there are no tests yet for the file you are concerned with, +you can just create the `.spec.ts` file and the framework will automatically +execute it. + +Let's go through the latter case. At the time of this writing, there was no +test specification for the `src/shared/defineFeatured.ts` file that provides +the specimens for the Featured Gallery. So, we simply created +`src/shared/__tests__/defineFeatured.spec.ts`. At the top of the file you need +to import the test facilities from `vitest`, and typically all of the exported +entities from the module being tested, like so: + +```typescript +{! ../src/shared/__tests__/defineFeatured.spec.ts extract: { stop: ibe..g } !} +``` + +In this case, there is only one export, `getFeatured`, so the test +specification is pretty brief. Here it is in full: + +```typescript +{! ../src/shared/__tests__/defineFeatured.spec.ts extract: { start: tured } !} +``` + +As you can see, tests are arranged in groups, each consisting of a call to +`describe`. The first argument is a string saying what is being tested in that +group, typically one of the exports. The second argument is a function that +executes the test. + +Within that function, each test is specified by a call to `it`. The first +argument is again descriptive, giving the property that is being tested. And +the second argument is again a function that runs the test. The body of this +function performs some trial computation using the item being tested, +typically followed by one or more assertions about the outcome of that +computation. The vitest framework provides a +[mini-language](https://vitest.dev/api/expect.html) for specifying assertions, +each triggered by a call to the `expect` function. You can see a few examples +in the code above (admittedly some a bit contrived for the sake of this +exposition). + +And that's all there is to it. With this spec file newly in place, +`npm run test:unit` reports three more passed tests than before. It's ready to +be committed and made part of a pull request. + +### Updating an existing end-to-end test + +If you modify an existing visualizer with an existing end-to-end test, you may +find yourself in the situation of having to update the end-to-end snapshots +(screenshots) to reflect the new expected behavior. To do so, first verify +that the only tests currently failing are those you expect. Then you can run +the command `npm run test:e2e -- --update-snapshots`. This will produce new +expected snapshots in the directory /e2e/tests, which you will need to add as +part of your commit. + +### Updating snapshots for the Github Continuous Integration (CI) testing + +Sometimes the new behavior will also affect the snapshots for tests that are +run automatically when pull requests (PR)s are created or updated, or new code +is merged into main. Note there is a separate set of snapshots for such tests, +because the images for some of the tests end up slightly different in the +Github CI runtime environment than on our local machines. + +If you're not sure whether you might be in this situation, you can tell after +you create a PR: if `npm run test:e2e` succeeds on your machine, but the +GitHub CI tests fail, and when you look at the details of the tests (see just +below for the procedure to find those details), the failures are caused by +snapshot mismatches for snapshots with file names that include `ci_snaps` in +the full path to the "expected" image. + +To go ahead and update these CI snapshots, the first challenge is to obtain +the actual images that were produced in the Github CI. For this, there is a +commented-out section in the Github CI specification file +`.github/workflows/ci.yaml`: + +```yaml +{! ../.github/workflows/ci.yaml extract: { start: playwright.test } !} +``` + +When you file your PR, or when you realize that you need to update the CI +snapshots, check in a "dummy" commit that uncomments this section (from the +`name:` line on). When you push that to Github, it will run the CI tests as +usual, but unlike the standard runs, it will save all of the actual output +files of the end-to-end test after it has completed. + +Once the CI test has completed (presumably failed, because why else would you +need to update snapshots?), navigate in the Github web interface to the page +for your pull request. At the bottom, you will see the test results, similar +to the following image: + +[](./img/ci-results.png) + +Click on the "Details" link for the failing test. It should automatically +scroll the output to the end of the tests (which will likely say "Error: +Process completed with exit code 1"), and then the next item in the outline of +the results should be "Extract snapshots", as in this screenshot: + +[](./img/extract-snapshots.png) + +Click on the right-pointing arrowhead at the left end of this line to drop +down the details of this "Extract snapshots" section. At the bottom of those +details will be an "Artifact download URL". Click on that link, and save the +resulting `ci_actual_snapshots.zip` ZIP file in a convenient temporary +location on your computer. + +Open or extract that ZIP file, as convenient, and navigate within its +directory structure to find the "...-actual.png" files with the images you +need. (You can find their exact paths in the detail report of the failed +test.) You should open them in an image viewer to make certain that they +reflect the new, desired behavior of the test. Save each one in place of the +corresponding "expected" snapshot in the +`e2e/tests/ci_snaps/[TEST_NAME_HERE].spec.ts-snapshots/` directory, noting +that you will have to remove the "-actual.png" suffix and replace it with +something like "-firefox-linux.png" depending on the browser and operating +system in question. + +When all of the expected files have been updated on your machine, go back to +the `.github/workflows/ci.yaml` file and put the comment characters back in +for the "Extract snapshots" block, as it is shown above. Make a new commit +with the updated snapshots and the reverted ci.yaml file, and push that to +your PR. Hopefully, the CI tests will now succeed and your PR will be back on +track for review. + +### Adding an end-to-end test + +The Playwright framework for end-to-end testing is broadly similar. In this +case, the spec files are all in the `e2e` directory, and again, they will +automatically be discovered and run when placed there. The file extension is +the same, and the main names are essentially arbitrary but should give an idea +of what's being tested. + +Let's take a look at one test in `e2e/gallery.spec.ts` that tests proper +operation of the page reached by clicking on the "Gallery" item in +Numberscope's navigation bar. As with vitest, there's a preamble importing the +test framework: + +```typescript +{! ../e2e/tests/gallery.spec.ts extract: { stop: before } !} +``` + +but this time there's not necessarily a need to explicitly import any of the +Numberscope code. That's because Playwright builds the entire system and opens +up its root URL in a browser running in "headless" mode -- that is to say, it +simulates rendering the resulting pages, and needs no attached display. The +Playwright tests manipulate that browser and verify it operates as expected. + +Next, we see a new test framework feature: the ability to run code before +every one of the tests in this file: + +```typescript +{! ../e2e/tests/gallery.spec.ts extract: { start: play, stop: describe} !} +``` + +You'll notice that this code takes a `page` argument. That's common to all +Playwright actions: page represents the browser tab that you are (virtually) +working with in your tests. In this case, we direct the tab to visit the +gallery URL, and we clear out its (simulated) browser-local storage to provide +a clean environment for testing. + +Note that vitest has a [similar ability](https://vitest.dev/api/#beforeeach) +to call a block of code before each test, if needed. + +Finally, let's look at the second test in this suite: + +```typescript +{! ../e2e/tests/gallery.spec.ts extract: + start: '(.*describe.*)' + stop: title +!} +... +{! ../e2e/tests/gallery.spec.ts extract: + start: '(.*minim.*)' + stop: clicking +!} +... +``` + +As with vitest, tests are arranged in groups, and here `test.describe` is the +direct analogue of `describe` in vitest, with `test` the analogue of `it`. The +second call to `test` in the "Gallery" group is shown above. Also like vitest, +it consists of a sequence of assertions, with reminiscent syntax. In this +case, most of the assertions have to be `await`ed, because they necessitate +various actions being performed in the browser, which may take some time to go +into effect. + +For example, the third item in the test is the direction to click on the first +element in the page with id `featured-arrow`. Whatever that does (it's +supposed to collapse the first group of images in the Gallery), it may take a +little while to occur and have its effects (who knows, it might load some +external webpage, which could take time). Hence the await. The test goes on to +check that the resulting page has the expected organization (correct elements +in the DOM tree), and then clicks again to see that the display goes back to +the way it was. + +Hopefully this example demystifies the process of creating a test for +Numberscope. You can also find +[more details](https://playwright.dev/docs/test-assertions) on the actions and +assertions you can put in a Playwright test. diff --git a/doc/css/.coordinate.css.generator b/doc/css/.coordinate.css.generator new file mode 100644 index 00000000..5562a9e2 --- /dev/null +++ b/doc/css/.coordinate.css.generator @@ -0,0 +1,17 @@ +{! ../../src/views/minor/NavBar.vue extract: + start: 'style' + stop: 'style' + replace: [ + ['^(.*)[$]tablet-breakpoint(.*)', '\1 800px\2'], + ['^(.*)[$]mobile-breakpoint(.*)', '\1 580px\2'] + ] +!} +{! ../../src/App.vue extract: + start: '[<]style[>]' + stop: '[<]/style[>]' + replace: [ + ['^(.*)[$]tablet-breakpoint(.*)', '\1 800px\2'], + ['^(.*)[$]mobile-breakpoint(.*)', '\1 580px\2'], + ['^(.*)(src: url..)[@](.*)', '\1\2/doc/src\3'] + ] +!} diff --git a/doc/extending.md b/doc/extending.md index 3d737f73..bde2aa7b 100644 --- a/doc/extending.md +++ b/doc/extending.md @@ -3,21 +3,21 @@ The main initial way that people excited by Numberscope's possibilities want to extend it is by creating their own Visualizer. Sometimes this addition is to tweak the behavior or add a feature to an existing Visualizer, or sometimes -it's a whole new idea on how to look at the universe of numbers. Whichever is +it's a whole new idea about looking at the universe of numbers. Whichever is the case for you, this guide will show you how to go about implementing a new Visualizer. Currently in order to do this, you must begin by setting up to run Numberscope -from source code on your own machine. (Rather than the public server -- -hopefully in the future Numberscope will switch to an architecture in which -you only need to have the code for your visualizer locally.) You also may want -to get set up with a convenient code editor for working with the program -you're going to create. +from source code on your own machine. (Rather than running from the public +server -- hopefully in the future Numberscope will switch to an architecture +in which the only code you need locally on your machine is the code for your +new visualizer.) You also may want to get set up with a convenient code editor +for working with the program you're going to create. -Once you have that working, you just need to add one code file to calculate -and display the images you want to create. Once that file is in the correct -place, you simply restart Numberscope and your visualizer will appear as one -of the options. +Once you have that working, you just need to add one source code file to +calculate and display the images you want to create. Once that file is in the +correct place, you simply restart Numberscope and your visualizer will appear +as one of the options. The details of these two main steps are on the following pages. If your visualizer ends up working well and you'd like to submit it back to the diff --git a/doc/gitting-it-right.md b/doc/gitting-it-right.md index 9cd90cb0..913f7ecb 100644 --- a/doc/gitting-it-right.md +++ b/doc/gitting-it-right.md @@ -7,10 +7,10 @@ use this guide to fix your setup. Here are the different scenarios you could find yourself in and what to do about them. -- 1: You have a clone of a Numberscope repository. +- 1: You have a clone of the official Numberscope repository. - 1.A: You haven't made changes. - [Create a fork](./working-with-git-and-github.md#create-a-fork). - - [Clone your fork](./working-with-git-and-github.md#clone-a-repo). + - [Add your fork as a remote](./working-with-git-and-github.md#add-a-remote). - [Create a branch](./working-with-git-and-github.md#create-a-branch). - [Push a branch](./working-with-git-and-github.md#push-a-branch). - Now you are working in your own fork on a dedicated feature @@ -27,7 +27,6 @@ about them. - [Create a fork](./working-with-git-and-github.md#create-a-fork). - [Add the remote of your fork](./working-with-git-and-github.md#add-a-remote). - [Push the branch to your fork](./working-with-git-and-github.md#push-a-branch). - - [Clone your fork](./working-with-git-and-github.md#clone-a-repo). - Now you are working in your own fork on a dedicated feature branch, congrats! Read about about basic Git operations below and get to work making a cool visualizer! @@ -37,7 +36,6 @@ about them. - [Create a fork](./working-with-git-and-github.md#create-a-fork). - [Add the remote of your fork](./working-with-git-and-github.md#add-a-remote). - [Push the branch to your fork](./working-with-git-and-github.md#push-a-branch). - - [Clone your fork](./working-with-git-and-github.md#clone-a-repo). - Now you are working in your own fork on a dedicated feature branch, congrats! Read about about basic Git operations below and get to work making a cool visualizer! @@ -47,42 +45,37 @@ about them. - [Create a fork](./working-with-git-and-github.md#create-a-fork). - [Add the remote of your fork](./working-with-git-and-github.md#add-a-remote). - [Push the branch to your fork](./working-with-git-and-github.md#push-a-branch). - - [Clone your fork](./working-with-git-and-github.md#clone-a-repo). - Now you are working in your own fork on a dedicated feature branch, congrats! Read about about basic Git operations below and get to work making a cool visualizer! - [Read about basic Git operations](./working-with-git-and-github.md#basic-operations). - 2: You have a fork of a Numberscope repository. - - 2.A: You haven't made changes. - - [Clone your fork](./working-with-git-and-github.md#clone-a-repo) - (if you don't have it on your computer yet). - - [Create a branch](./working-with-git-and-github.md#create-a-branch). - - [Push a branch](./working-with-git-and-github.md#push-a-branch). - - Now you are working in your own fork on a dedicated feature - branch, congrats! Read about about basic Git operations below and - get to work making a cool visualizer! - - [Read about basic Git operations](./working-with-git-and-github.md#basic-operations). - - 2.B: You have made changes. - - 2.B.1: You are working on the main branch. - - 2.B.1.A: You made commits. - - Ask someone at Numberscope for help. - - 2.B.1.B: You haven't made commits. - - [Stash your changes](./working-with-git-and-github.md#stash-your-changes). - - [Create a branch](./working-with-git-and-github.md#create-a-branch). - - [Unstash your changes](./working-with-git-and-github.md#unstash-your-changes). - - [Push a branch](./working-with-git-and-github.md#push-a-branch). - - Now you are working in your own fork on a dedicated - feature branch, congrats! Read about about basic Git - operations below and get to work making a cool visualizer! - - [Read about basic Git operations](./working-with-git-and-github.md#basic-operations). - - 2.B.2: You are working on a different branch. - - 2.B.2.A: You have made commits. - - This is the correct setup! Congrats. Feel free to - [read about basic Git operations](./working-with-git-and-github.md#basic-operations). - - Keep working in this fashion. - - 2.B.2.B: You haven't made commits. - - [Commit your changes](./working-with-git-and-github.md#commit-changes). - - Now you are working in your own fork on a dedicated - feature branch, congrats! Read about about basic Git - operations below and get to work making a cool visualizer! - - [Read about basic Git operations](./working-with-git-and-github.md#basic-operations). + - 2.A: You haven't cloned anything. + - [Clone the official Numberscope repository](./working-with-git-and-github.md#clone-a-repo) + - Proceed with 1.A above, except you won't need to create a fork. + - 2.B: You cloned the official Numberscope repository. + - Proceed with 1. above, except you won't need to create a fork on + any step where it says you should. + - 2.C: You cloned your fork. + - Here you have a choice. You can either keep working this way, and + add the official Numberscope repository + [as a remote](./working-with-git-and-github.md#add-a-remote) + (usually named `upstream`). Then in all other tutorials in our + documentation where it says `origin` you would use `upstream` and + where it says `fork` you would use origin. + - Or, if that is too confusing, you could reconfigure to the + recommended situation in which you clone the official repository + and then add your fork as a remote. If you haven't made any + changes, you can just delete your clone, and start again with 2.A. + above. + - If you have made changes, you will have to rearrange your remotes. + [Check your remotes](./working-with-git-and-github.md#check-your-remotes). + If you have a remote named `fork` that does not point to your fork + (unlikely, but check just in case), remove that remote with + `git remote remove fork`. Then add your fork + [as a remote](./working-with-git-and-github.md#add-a-remote) named + `fork`, even if it is already present as origin. Then execute + `git remote remove origin`. Finally, add the standard Numberscope + repository as a remote named `origin`. Then, you can proceed with + 1.B. above, except skip any steps that instruct you to create a + fork or add a remote. diff --git a/doc/husky-pre-commit.md b/doc/husky-pre-commit.md index 059a2e07..91556195 100644 --- a/doc/husky-pre-commit.md +++ b/doc/husky-pre-commit.md @@ -6,9 +6,16 @@ Assuming that the Git hooks specified by this repository have been actions prior to every call to `git commit`: ```sh -{! ../.husky/pre-commit extract: { start: list } !} +{! ../.husky/pre-commit extract: { start: husky } !} ``` +See the page on [package manager scripts](working-with-pm.md) for the meanings +of most of these actions; the `cleancommit` action uses custom code in the +tools/ directory to verify that there are no unstaged changes or untracked +files, which would confuse the later steps (they would be analyzing the +working tree rather than the staged file, so we just enforce those are the +same). + ([Direct link](../.husky/pre-commit) to the Husky pre-commit script.) diff --git a/doc/img/ci-results.png b/doc/img/ci-results.png new file mode 100644 index 00000000..9f7826eb Binary files /dev/null and b/doc/img/ci-results.png differ diff --git a/doc/img/extract-snapshots.png b/doc/img/extract-snapshots.png new file mode 100644 index 00000000..f6126de8 Binary files /dev/null and b/doc/img/extract-snapshots.png differ diff --git a/doc/making-a-visualizer.md b/doc/making-a-visualizer.md deleted file mode 100644 index de36c67d..00000000 --- a/doc/making-a-visualizer.md +++ /dev/null @@ -1,279 +0,0 @@ -# Building a Visualizer - -We'll start with a general outline of creating visualizers, and then provide -some detailed examples and guidance. - -## An overview of the process - -#### Grab a template for your chosen graphics framework - -The easiest way to build a visualizer is to extend a pre-made visualizer base -class, which automatically sets up a graphics framework for you to use. Right -now, there's only one base class available: - -- [`P5Visualizer`](#making-a-p5-visualizer-in-detail) uses the - [**p5.js**](https://p5js.org/learn/) library for graphics and user - interaction. - -For a quick start, copy and modify the template file for your chosen -framework, which you can find in `src/visualizers-workbench`. - -If you want to use a new graphics framework, you'll need to write your own -implementation of the [visualizer interface](behind-the-scenes.md). - -#### Document as you write - -Each visualizer has its documentation woven into the source code, using -special `/** md` … `**/` comments that are automatically compiled -into a [documentation page](../src/visualizers/Differences.md). We have some -[conventions](#how-to-document-your-visualizer) for what the documentation -comments should include and where in the source code they should go. - -#### Develop your visualizer on the workbench - -While you're working on a visualizer, we recommend keeping it in the -`src/visualizers-workbench` directory, where frontscope typically won't notice -it. To load it, run frontscope in "workbench mode" by calling -`npm run dev:workbench`. - -When you're ready to propose your visualizer as an official part of -Numberscope, you'll move it to `src/visualizers`. Then frontscope will notice -and load it when you call the usual `npm run dev`. For details, see -[below](#where-to-put-your-visualizer). - -A visualizer, like a work of art, is never really finished—even when it's -accepted into Numberscope. Even the humble template visualizer discussed below -could be extended. You could shorten the infinite progress bar for finite -sequences, allow fast navigation by holding down arrow keys, add a progress -bar mouse-over that shows the index… the possibilities are endless. We invite -you to try extending or enhancing existing Visualizers as well as building -your own. For now, though, let's return to the process of making a new -visualizer based on p5. - -## Making a p5 visualizer, in detail - -A good way to start a p5 visualizer is to copy and modify one of the basic -examples in the `src/visualizers-workbench` directory: - -- `P5VisualizerTemplate.ts` -- `Differences.ts` - -Let's look at the parts of a p5 visualizer. We recommend following along in -one of the basic examples as you read. - -#### 🔑️ Name _(required)_ - -Put the visualizer's name in the static `visualizationName` property. The name -is used in the visualizer list and the titles of bundle cards. - -#### 💡️ Parameters _(often used)_ - -Parameters are the user-facing structures that ask for control values when a -visualizer is created. When someone running your visualizer sets a parameter -in the graphical interface (UI), its value is written to a corresponding -top-level property of the visualizer object. Conversely, if your code -internally changes one of those top-level properties while the visualizer is -running, you should refresh the corresponding parameter by calling -`this.refreshParams()` so the person interacting with it can see the change. - -Below the list of these parameter properties, the visualizer class also needs -a `params` property that describes how the parameters should appear in the UI. -Look in `src/shared/Paramable.ts`, or in other visualizers, to learn about the -options you can set in the `params` property -- labels for the parameter, -allowed values for a dropdown menu, and so on. - -- **p5 Template:** `stepSize`. -- **Differences:** `n`, `levels`. This visualizer uses `refreshParams()` - when it auto-fills the optional parameter `levels`. - -#### 💡️ Other properties _(often used)_ - -You may also need internal data (top-level properties that are set and updated -while the visualizer is running, beyond the user's direct control). These -properties might do things like: - -- Keeping track of which part of the sequence you're looking at. -- Storing colors that are created during setup. -- Remembering where an animated object is. - -By convention, we list these properties after the `params` property. - -- **p5 Template:** `index`. -- **Differences:** `first`. - -#### 💡️ Determine if parameters are consistent _(often used)_ - -When the user clicks "save", `checkParameters()` is called, giving you a -chance to check the parameter values and prompt the user to correct any -invalid ones. - -Your validation can't depend on the sequence, because there might not be any -sequence loaded when the validation check runs. You can do sequence-dependent -validation in [`setup()`](#set-up-the-visualizer-often-used), as described -below, but you won't be able to prompt the user for corrections at that point. - -- **p5 Template:** Make sure that the step size is positive. -- **Differences:** Make sure that the number of terms is positive or zero, - the number of levels is positive, and the number of terms is at least the - number of levels. - -#### 🔩️ Inhabit a page element _(advanced)_ - -Each time the visualizer is inserted into a page element, the `inhabit()` -function is called, giving you access to the element the visualizer is about -to inhabit. If you don't need information from the web page your visualizer is -running in, or access to its document object model (DOM), you shouldn't need -to implement `inhabit()`. The full details on this method are in the -[visualizer interface](behind-the-scenes.md) documentation. - -#### 💡️ Set up the visualizer _(often used)_ - -When the p5 graphics context becomes available, `setup()` is called, giving -you your first chance to set graphics options and draw on the canvas. This is -a good place for one-time graphics operations, like painting the background of -a static visualizer or setting colors and stroke options that won't change -from frame to frame. - -If you implement `setup()`, start by calling `super.setup()`, which includes -the [`createCanvas()`](https://p5js.org/reference/#/p5/createCanvas) call that -must appear in every p5 setup function. - -When `setup()` is called, the visualizer will always be attached to a -sequence, so you can do sequence-dependent validation and initialization here. - -- **p5 Template:** Go to the beginning of the sequence. Create palette - colors. Set text alignment. -- **Differences:** Fill in the default value of the `levels` property if it - was left unset in the parameters dialog. Do a consistency check between - the sequence being visualized and the parameters. - -#### 🔩️ Show or stop the visualization; depart from a page element _(advanced)_ - -You shouldn't need to implement `show()`, `stop()`, or `depart()`. You can -learn about them from the [visualizer interface](behind-the-scenes.md) -documentation, and from how they're implemented in the `P5Visualizer` base -class. - -#### 🔑️ Draw the visualization _(required)_ - -The `draw()` function is called on each frame, giving you a chance to draw -your visualization! Look at the examples and the p5 -[tutorials](https://p5js.org/learn/) and -[p5.js reference](https://p5js.org/reference/) to learn about what you can do. - -Drawing tools and options are found in the graphics context, `this.sketch`. -Accessing `this.sketch` triggers some consistency checks, so we recommend -storing its value as a local constant in each function call that uses it. This -avoids redundant checks. - -Sequence information is found in `this.seq`. This is a `SequenceInterface` -object, so it will always have a method `getElement(n)` that returns the `n`th -entry in the sequence. - -You have to implement the `draw()` method, even if it does nothing. Your -visualizer can't be loaded into Numberscope without it. - -#### 💡️ Stop and start animation _(often used)_ - -If your visualization is a static picture, call the sketch's `noLoop()` -function when you're done drawing. This stops the animation loop, so you don't -waste time re-drawing the same picture dozens of times per second. If the -visualization is only static temporarily, you can call `loop()` to re-start -the animation loop whenever you need to. - -- **p5 Template:** Stop the animation loop at the end of each frame. Only - start it again when the user steps to a different sequence entry, - requiring the picture to be re-drawn. -- **Differences:** Draw the whole visualization in one frame, and then stop - the animation loop. - -#### 💡️ Respond to user interactions _(often used)_ - -Each time the user interacts with your visualization, an -[event handling](https://p5js.org/reference/#group-Events) function like -`keyPressed()` or `mouseClicked()` will be called, giving you a chance to -respond. There are handlers for a wide variety of input events. - -#### 🔑️ How to make your visualizer available _(required)_ - -The engine expects the visualizer to be packaged in a `VisualizerExportModule` -object, constructed from the visualizer class and a short description string. -This "export" happens after the class definition. - -#### 🔩️ How to handle errors _(advanced)_ - -There are two ways to let people interacting with a visualizer know that -something unexpected has happened. The first is with the -[alertMessage](../src/shared/alertMessage.md) utility. The second is to just -throw an error. If it's not caught anywhere else, the visualizer framework -will show it in an error dialog. - -### How to document your visualizer - -The p5 Template visualizer and the Differences visualizer both follow our -documentation conventions. - -#### Name, sample image, and description - -In a documentation block just before the visualizer class definition, put a -level-1 heading with the visualizer's name, followed by a sample image and a -description. - -To make the sample image, take a screenshot of your visualizer and put it in -the `src/assets/img` directory. Add the image to the -documentation page as shown in the examples, using the `width` property for -scaling. - -Below the sample image, describe what the visualizer does. - -#### Parameters - -In a documentation block just before definition of `params`, put "Parameters" -in a level-2 heading. Inside the definition of `params`, just before each the -parameter's key, put a documentation block that displays like this: - -> **Parameter name:** Description of the parameter. _(Type and constraints.)_ - -#### Controls - -If users can interact with your visualizer while it's running, these -interactions should be documented too. In a documentation block just before -the first event handling method, put "Controls" in a level-2 heading. Display -control information like this: - -> **Key or gesture:** Description of effect. - -You can organize the control documentation comments in whatever way makes the -most sense. If possible, order the event handling methods so that most -controls can be documented near the method that implements them. - -#### Additional documentation - -Any additional documentation should go in documentation comments after all of -the event handling methods. This is the place for in-depth discussions, -example settings, image galleries, credits, and so forth. - -Code-related documentation, like explanations of algorithms used in the -visualizer, should go as close as possible to the related code. This means, in -particular, that code requiring additional documentation should come after the -event handling methods if possible. - -#### Export block - -The `VisualizerExportModule` block should be the last thing in the visualizer -source file. That makes it easy to find. - -## Where to put your visualizer - -However you made it, when a visualizer is ready for Numberscope users, place -the file containing its class definition and export module in the folder -`src/visualizers`. When the frontscope client runs, it'll find your visualizer -and compile it at runtime. - -The visualizers in `src/visualizers` will appear as options when you start -Numberscope with `npm run dev` as usual. - -As discussed [earlier](#develop-your-visualizer-on-the-workbench), visualizers -that aren't ready for Numberscope users should go in -`src/visualizers-workbench`. You can load them to see how they work by running -with `npm run dev:workbench`. diff --git a/doc/onboarding.md b/doc/onboarding.md index 1e0a586c..33387ce9 100644 --- a/doc/onboarding.md +++ b/doc/onboarding.md @@ -7,7 +7,9 @@ contributors. Unless otherwise specified, all commands we ask you to run are supposed to be run in a Terminal emulator using a shell like Bash or Zsh (on Linux and macOS) -or in Git Bash (on Windows). +or in Git Bash (on Windows). Anything you have to fill in with the proper +information for your setup (as opposed to execute verbatim as shown) is +enclosed in brackets `[]`. ## Accounts, etc. @@ -16,7 +18,6 @@ Boulder Experimental Mathematics Lab.) 1. If you don't have an account, sign up for [GitHub](https://github.com). 2. Make sure you've been added to the Numberscope GitHub organization. -3. Make sure you've been added to the chat app (Slack or Zulip). ## Setting up your computer for development @@ -38,9 +39,14 @@ Boulder Experimental Mathematics Lab.) 3. If you don't have [NodeJS](https://nodejs.org/en/) installed, install it. NodeJS allows you to run JavaScript outside of a web browser. We use it in our front end code base. -4. If you don't have [Python 3](https://www.python.org/) installed, install +4. Make sure you have [Make](https://linuxhandbook.com/using-make/) installed. + There's a guide for Linux on that page; or you can try these other links + for + [Windows](https://stackoverflow.com/questions/32127524/how-to-install-and-use-make-in-windows) + or [MacOS](https://stackoverflow.com/questions/1469994/using-make-on-os-x). +5. If you don't have [Python 3](https://www.python.org/) installed, install it. We use Python in frontscope for our documentation site. -5. Make sure you have a `venv` module by running the following command: +6. Make sure you have a `venv` module by running the following command: ```sh python3 -m venv -h ``` @@ -55,46 +61,51 @@ Boulder Experimental Mathematics Lab.) commands you run are run using the dependencies in the virutal environment, and not the dependencies you installed elsewhere on your computer. -6. Somewhere on your computer, make a directory where you can keep Numberscope +7. Somewhere on your computer, make a directory where you can keep Numberscope code. I like to put a directory called `Code` in my home directory. You can call this whatever you want. -7. If you plan to submit new code to become part of Numberscope at some time - in the future, _you must_ "fork" (make your own copy of) the repository: - - Go to https://github.com/numberscope/frontscope. - - Click the "Fork" button (in the upper right as of this writing) and then - follow the instructions GitHub provides. You need to create the fork on - your GitHub account. If you just want to run from source it's not - strictly necessary, and you can fork later, it's just a little more - complicated. If you want your own fork: - ```sh - cd /path/to/your/code/directory/ - git clone https://github.com/{your-github-username}/frontscope.git - ``` - Otherwise, if you don't want to fork right now: +8. Clone the official github repository: ```sh - cd /path/to/your/code/directory/ + cd [/path/to/your/code/directory] git clone https://github.com/numberscope/frontscope.git ``` If you have trouble, read [this doc](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) or ping someone for help. -8. Go to the newly cloned `frontscope` directory and install the dependencies: +9. If you plan to submit new code to become part of Numberscope at some time + in the future, _you must_ also "fork" (make your own copy of) the + repository: + - Go to https://github.com/numberscope/frontscope. + - Click the "Fork" button (in the upper right as of this writing) and then + follow the instructions GitHub provides. You need to create the fork on + your GitHub account. +10. If you are going to submit new code, you will also need to run the + standard tests of the system with your changes in place, and likely add + new tests for your changes. Running tests requires that you have + [Docker](https://www.docker.com/) installed on your system. There are many + tutorials for installing docker; here are example ones for + [Windows](https://docs.docker.com/desktop/install/windows-install/), + [Ubuntu Linux](https://linuxconfig.org/quick-docker-installation-on-ubuntu-24-04), + and + [Mac OS](https://thesecmaster.com/blog/installing-docker-desktop-on-macos). +11. Go to the newly cloned `frontscope` directory and install the + dependencies: ```sh cd frontscope npm install ``` You need NodeJS installed to do this. -9. Run the development server (this runs a local copy of Numberscope on your - computer so you can interact with the webpage): +12. Run the development server (this runs a local copy of Numberscope on your + computer so you can interact with the webpage): ```sh npm run dev ``` This should print a link that you can open in the browser. Open it and see if Numberscope seems to be working. -10. If you plan on contributing code to Numberscope, _you must_ work in your +13. If you plan on contributing code to Numberscope, _you must_ work in your fork on a dedicated feature branch. To learn how to create a branch, see - [this doc](./working-with-git-and-github.md#create-a-branch). -11. Finally, before you start changing code, please read + [this doc](working-with-git-and-github.md#create-a-branch). +14. Finally, before you start changing code, please read [our docs on submitting a pull request](../CONTRIBUTING.md#submit-a-pull-request). See [the doc on running from source](./running-from-source.md) for more @@ -102,4 +113,4 @@ information. ## Making a visualizer -Now that you're set up, try [making a visualizer](making-a-visualizer.md)! +Now that you're set up, try [making a visualizer](visualizer-overview.md)! diff --git a/doc/pull-request-checklist.md b/doc/pull-request-checklist.md index 845f6f04..9c48bb4f 100644 --- a/doc/pull-request-checklist.md +++ b/doc/pull-request-checklist.md @@ -5,37 +5,60 @@ The PR submitter should: - Please use a `snake_case` name for the branch in the PR. -- Please limit your PR's focus to one thing. +- Please limit the PR's focus to one thing. - Ideally, submit a small PR with limited functionality that can be built upon instead of submitting one huge, monolithic PR. Typically, the smaller - the PR, the better, with the obvious caveat that the code in your PR needs + the PR, the better, with the obvious caveat that the code in the PR needs to work and actually do something. A small PR for a visualizer might be in the ballpark of 200 lines of code. -- Write or supplement test(s) for the file(s) you touch in your PR. +- Write or supplement test(s) for the file(s) affected by the PR. In + particular, if it is a bug fix PR, there **must** be a new test that would + fail with the prior code, but passes in the PR. The new tests may be + either unit (vitest) or end-to-end (Playwright) tests or both, as + appropriate. +- Update all documentation to reflect the changes in the PR. - Make sure Numberscope runs properly, including former supposedly unmodified behaviors and newly implemented ones. In particular, run it via 'npm run dev' and with the browser console open and make sure there are no log messages from Numberscope code, warnings, or errors. +- Make sure that the branch in the PR is based on the latest commit in the + `main` branch in the + [official frontscope repository](https://github.com/numberscope/frontscope). + If not, [rebase it](./working-with-git-and-github.md#rebase-your-branch). - Read over the reviewer checklist and try to make sure in advance that your code is going to proceed as smoothly through the review as possible. ## For PR reviewers +- The new code is readable, meets our principles, and of good quality. - All new or changed features are appropriately documented. -- Tests are appropriately modified for all new or changed features. +- Tests are appropriately modified for all new or changed features. If it is + a bugfix PR there must be at least one new test. Most other PRs should + have new or changed tests as well. So if you do not see any changed files + in the `e2e/tests` directory or in any `__tests__` directory, that is at + least an "orange" flag. - The PR is passing lint by running `npm run lint`. There should be no changed files and no warnings/errors. +- The PR passes all tests. Note that you must run the end-to-end tests + yourself with `npm run test:e2e`. You can't rely solely on the GitHub CI + test results; for example, as of this writing, they are unable to run any + WebGL tests. In other words, consider passing the GitHub CI tests as a + necessary but not sufficient condition for considering a PR to be "passing + all tests". - The PR builds by running `npm run build`. (This also checks type correctness.) There should be no errors, and for now the only allowed warning is the one about some assets being too big. -- The PR passes all tests. Right now (Oct. 2022), just by running - `npm run test:unit`. -- Numberscope runs properly -- basically the same check as on the submitter - list, but be sure to exercise as many randomly selected behaviors as you - have time for, definitely including but not limited to the ones nominally - affected by the PR. This should be done in `npm run preview` mode after a - successful build. -- At the end of the review process, before merging, add a commit to update - the - ["Contributors" section of the "About" document](about.md#contributors) to - include the submitter's name, if it is not already present. +- The built system runs with `npm run preview`. +- When run that way, Numberscope operates properly. The end-to-end tests + take the pressure to try a variety of random behaviors off of the + reviewer, but a number of possible actions that seem related to the + changes in the PR must definitely be tried in this fashion before + approving for merge. +- In particular, if there is a new or significantly altered visualizer or + sequence, running it on a sequence with (say) a million entries (but not + infinitely many, since that case is more typically caught) does not + hang/block the browser. + +At the end of the review process, before merging, add a commit to update the +["Contributors" section of the "About" document](about.md#contributors) to +include the submitter's name, if it is not already present. diff --git a/doc/running-from-source.md b/doc/running-from-source.md index 187aed28..dc43cd65 100644 --- a/doc/running-from-source.md +++ b/doc/running-from-source.md @@ -1,22 +1,31 @@ # Setting up to run from source 1. Prerequisites: To install and run properly, `frontscope` needs - [Git](https://git-scm.com/), [Node.js](https://nodejs.org/en/), and - [Python](https://www.python.org/) (version 3.5 or later) with a working + [Git](https://git-scm.com/), + [make](https://linuxhandbook.com/using-make/), + [Node.js](https://nodejs.org/en/), and [Python](https://www.python.org/) + (version 3.5 or later) with a working "[venv](https://docs.python.org/3/library/venv.html)" module. If any of these are not present on your system, install them. It's very likely you already have Python, but on Debian/Ubuntu systems, you may well have to - install the venv module with a command like `apt install python3.8-venv` - (you may have to replace the "3.8" with the version that is currently + install the venv module with a command like `apt install python3.11-venv` + (you may have to replace the "3.11" with the version that is currently running in your installation). -2. Clone frontscope to an appropriate location on your computer, and switch +2. In addition, if you want to build the documentation website for the + `frontscope` system, you will need the [d2](https://d2lang.com) diagram + generation software installed on your system. This step is not necessary + just to run `frontscope` itself using the `npm run dev` command shown + below; but it is needed to build the production version of `frontscope` + and/or to run the end-to-end [tests](code-tests.md) which are required + before [contributing code](../CONTRIBUTING.md) to Numberscope. +3. Clone frontscope to an appropriate location on your computer, and switch into the new repository's top-level directory: ```sh cd /where/you/keep/your/code/ git clone https://github.com/numberscope/frontscope.git cd frontscope ``` -3. If you will be connecting to an instance of `backscope` (for obtaining +4. If you will be connecting to an instance of `backscope` (for obtaining information about OEIS sequences) running locally on your machine, then create a `.env.local` file to override the `.env` from the distribution: ```sh @@ -25,7 +34,7 @@ ``` (When you want to go back to connecting to the standard backend server, remember to delete your `.env.local` or move it out of the way.) -4. Install dependencies: +5. Install dependencies: ```sh npm install ``` @@ -33,7 +42,7 @@ [Husky](https://github.com/typicode/husky). For a comprehensive list of what commands are run when you `git commit` -- typically linting and testing -- see [Husky actions](husky-pre-commit.md).) -5. Compile and start a server running frontscope, with hot-reloading for +6. Compile and start a server running frontscope, with hot-reloading for development: ```sh npm run dev @@ -41,12 +50,16 @@ The output of this command will provide instructions for connecting to the new running instance of frontscope with your browser. +There are a number of [other `npm` scripts](working-with-pm.md) that will let +you do things like generate the documentation pages or run tests on the code, +as well. + ## Adding to and modifying code -To add to the code, you need to use what's called an "editor" or an -"integrated development environment" (IDE) to help you enter the commands, +To add to the code, you will need to use either what's called an "editor" or +an "integrated development environment" (IDE) to help you enter the commands, find any problems, and get your idea working. There are many possibilities for -these tools, but if you're just starting out, Numberscope recommends an IDE -called "VSCode" with some additional features added by plugins (Volar and a -TypeScript Vue module for it). The details for this part of the setup are in -the [Contributing](visual-studio-code-setup.md) section. +these tools. One option is an IDE called "VSCode" with some additional +features added by plugins (Volar and a TypeScript Vue module for it). The +details for this part of the setup are in +[a separate page](visual-studio-code-setup.md). diff --git a/doc/server-administration.md b/doc/server-administration.md index 26aa5671..777df100 100644 --- a/doc/server-administration.md +++ b/doc/server-administration.md @@ -21,9 +21,34 @@ subdirectory of the frontscope clone go live instantly. ## How to deploy a new version of frontscope +We presume that we are only going to install the current version of the `main` +branch from the standard repository onto the production machine. This version +has the advantages of having undergone code review and automated unit and +end-to-end tests. Nevertheless, before installing any version, you should +manually make sure it seems to be working properly, one last time: + +1. Make sure you have the latest version of `main` from the standard + repository pulled and checked out on your own machine (or in any case, a + non-production computer). +2. Execute `npm run test:e2e`. +3. Assuming all passes, execute `npm run preview`. +4. Connect to the provided URL, and try a few things with the resulting + Numberscope instance that you've never tried before, and a couple that you + have done many times, to make sure that all seems to be operating. + +When you've done all that, it's OK to install that version on the official +numberscope server. + 1. Log into the numberscope server and `cd ~scope/repos/frontscope`. 2. Execute `sudo -u scope git pull`. 3. Execute `sudo -u scope npm install`. 4. Execute `sudo -u scope npm run build`. -5. Check that the numberscope front end at the primary server URL is working - and serving the updated version of Numberscope. +5. Execute `sudo systemctl restart numberscope`. Note that this step is may + generally be unnecessary for the service to update to the new version, but + is good hygeine for the server in any case. +6. Execture `sudo systemctl status numberscope` to verify that the server + believes the service is running. +7. Check that the numberscope front end at the primary server URL is working + and serving the updated version of Numberscope (by browsing to that URL and + again trying some tasks, especially ones that exercise any new features in + the version you have just installed). diff --git a/doc/user_guide.md b/doc/user_guide.md index 0eecdc9a..9ec40e58 100644 --- a/doc/user_guide.md +++ b/doc/user_guide.md @@ -5,10 +5,13 @@ itself with either of the buttons mentioning "the 'Scope." (There's one in the top navigation bar and one right in the center of the page for convenience.) That will bring you to: -{! ../src/views/Scope.vue extract: { start: ''} !} +{! ../src/views/Scope.vue extract: { start: ''} !} ## Errors If Numberscope experiences an error, you might see an alert with the error message. If you see one that does not resolve upon reloading the page and -trying again, please email the address shown with the error message. +trying again, please let us know about the problem by +[filing an issue on GitHub](https://github.com/numberscope/frontscope/issues/new). +Please describe as thoroughly as you can what you were doing when the alert +ocurred and what it said -- a screenshot could be helpful. Thanks! diff --git a/doc/visual-studio-code-setup.md b/doc/visual-studio-code-setup.md index 01f20f10..61572a39 100644 --- a/doc/visual-studio-code-setup.md +++ b/doc/visual-studio-code-setup.md @@ -5,7 +5,7 @@ to develop code contributions to Numberscope. ## VS Code -The recommended IDE consists of [VSCode](https://code.visualstudio.com/) + +One potential IDE consists of [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin). @@ -54,5 +54,6 @@ There are of course many other possible editors and IDEs. For example, one can use the venerable Emacs editor, which has many packages for highlighting code and autoformatting it. Support for the latter that respects Numberscope's particular setup is provided by `tools/editor/autoformat.el` in this -repository. That Emacs package runs source files through the `prettier-eslint` -formatter (see the following page) every time they are saved in Emacs. +repository. That Emacs package runs source files through combined `prettier` +and `eslist` formatting (see the following page) every time they are saved in +Emacs. diff --git a/doc/visualizer-basics.md b/doc/visualizer-basics.md new file mode 100644 index 00000000..ee156141 --- /dev/null +++ b/doc/visualizer-basics.md @@ -0,0 +1,369 @@ +# The basics + +A good way to start a p5 visualizer is to copy and modify the basic example in +the `src/visualizers-workbench` directory: + +- [`P5VisualizerTemplate.ts`](https://github.com/numberscope/frontscope/blob/main/src/visualizers-workbench/P5VisualizerTemplate.ts) + +On this page we look at all of the core parts that go into this basic p5 +visualizer, as an introduction. The [following page](visualizer-in-depth.md) +will provide a complete summary of all of the methods and data properties you +can use to implement more involved features and to handle additional +possibilities that might come up in the use of a visualizer. + +Let's start with a diagram showing the basic lifecycle of a visualizer: + +````d2 layout="elk" +direction: right +classes: { + method: { + height: 40 + style: { + border-radius: 10 + font-size: 20 + } + } +} + +Start: "" { + style: {fill: blue} + shape: circle + width: 10 +} +Init: |md + ### Create visualizer object + ```txt + property initializers, + constructor + ``` +| +Start -> Init: | page load/ +visualizer change| +SPC: |md + ### Parameter checks + ```txt + individual + validate() functions + ``` +| +Init -> SPC +Setup: "setup()" {class: method} +SPC -> Setup +Draw: "draw()" {class: method} +Setup -> Draw +Draw -> Draw: {target-arrowhead: "[stop()/continue()]"} +Draw -> SPC: "change parameter" {style.font-size: 18} + +legend: { + near: bottom-center + label.near: top-left + direction: right + method: "classMethod()" { + class: method + height: 20 + style: { + opacity: 0.6 + font-size: 14 + } + } + otherCode: | + ### Other code section + ```txt + details + ... + ``` + | { + width: 200 + style: { + opacity: 0.6 + font-size: 12 + } + } + method -> otherCode: "action/event" {style.font-size: 14} + style { + opacity: 0.4 + font-size: 18 + } +} +```` + +When someone visits a Numberscope URL that uses a particular visualizer, or +selects it from the Visualizer Switcher popup or from a Specimen Gallery, then +the frontscope system has to create the Visualizer class object, which in +TypeScript language terms means to execute the statements in its class +definition that assign initial values to its properties, and call its +constructor method. + +Then the values of the parameters specifying the details of the behavior of +that visualizer have to be checked, to make sure they make sense. The +frontscope system does some of this automatically; for example, it makes sure +that a parameter you've designated as an integer doesn't have any decimal +part. The primary way you add more checks is with `validate()` functions +included in the descriptions of the parameters of your visualizer. + +Once all of the parameters check out, the system creates a p5 drawing canvas +for you, and as typical for p5, calls your `setup()` method once and then +starts a loop calling your `draw()` method repeatedly to "paint" each frame of +your visualization on the screen (under the presumption that what you want to +display changes over time; there's +[discussion below](#stop-and-start-animation-often-used) of what to do if +not). Once this drawing loop is underway, it can be stopped and started with +`stop()` and `continue()` methods (which are provided for you by the +`P5Visualizer` base class). Moreover, if any of the parameters are changed +through their controls in the browser user interface (UI), the system will go +back to the validation step and reinitialize the process from there. (So in +particular, it will not re-run the property initializers or class constructor +method.) + +In the next section we go through all of these aspects of the visualizer code +a bit more systematically, in the order in which they appear in +[`P5VisualizerTemplate.ts`](https://github.com/numberscope/frontscope/blob/main/src/visualizers-workbench/P5VisualizerTemplate.ts). +You may want to follow along in that code. + +## Core parts of a visualizer implementation + +### 💡️ Parameters _(often used)_ + +- In **P5VisualizerTemplate:** `stepSize` + +Parameters are the data items that control the details of the behavior of your +visualizer. The frontscope system creates an input element (like a text box or +checkbox) for each of them in its UI. When someone running your visualizer +sets a parameter through the UI, its value is written to a corresponding +top-level property of the visualizer object (with the same name as the +parameter). + +The parameters are described in the `paramDesc` object, usually defined just +before the declaration of your class derived from `P5Visualizer`. The keys of +this object are the names of the parameters. The values are inner objects that +represent various different characteristics of the parameter. Each parameter +must specify its `type` (for example, `ParamType.BIGINT` if the parameter will +take on a TypeScript `bigint` value), its `default` value (e.g., `1n` for a +`BIGINT` parameter), its primary designation `displayName` in the UI (e.g., +`Step size`), and whether it is `required` to be filled in. There are many +optional parameters, such as a longer `description` that may be shown +alongside the input element for the parameter, or popped up as a tooltip when +the mouse pointer hovers on the parameter's entry area. For a complete list of +the options you can set and what they do, check +[this page](../src/shared/Paramable.md). + +Note that you pass the `paramDesc` object as an _argument_ to the base class +`P5Visualizer` when you declare your class. + +#### 💡️ Determine validity of parameter values _(often used)_ + +In particular, if you have additional checks you want to do on a parameter +value before you accept it and start displaying your image, the easiest thing +to do is define a `validate()` function in the options object for your +parameter. The `P5VisualizerTemplate` does this for `stepSize` to make sure it +is positive. Note that in a `validate()` function, you receive the potential +value of the parameter as the first argument, along with a `ValidationStatus ` +object _status_. If you find a problem with the proposed value, you should +call `.addError()` on `status`. On the other hand, if there's merely something +unusual or possibly the matter with the value, you may prefer to call +`.addWarning()` on the `status` which will display a message in the UI but +still allow your visualizer to proceed. + +### 🔑️ Name _(required)_ and description + +- In **P5VisualizerTemplate:** `category` and `description` + +Put the visualizer's name in the static `category` property of your Visualizer +class; this is just a string value. It's encoded in this way because +individual visualizers don't have distinct names, just the class of Visualizer +you are creating does. A brief text `description` can also be provided, again +as a static property. The name (`category`) and `description` are used when +presenting visualizers to the user to choose from. + +### 💡️ Other data properties _(often used)_ + +- In **P5VisualizerTemplate:** `index`, `bgColor`, `textColor`, and + `outlineColor` + +You may also need internal data (top-level properties that are set and updated +while the visualizer is running, beyond the user's direct control). These +properties might do things like: + +- Keeping track of which part of the sequence you're looking at. +- Storing colors that are created during setup. +- Remembering where an animated object is. + +By convention, we list these properties after `category` and `description`. By +the rules of the TypeScript language, you must at least specify the type of +each property; but whenever possible, it is convenient to set an initial +placeholder value for the property. In the case of colors, p5 does not provide +a way to create a color until the sketch is initialized, a bit later in the +process. So you can initialize any color properties you may have with +`INVALID_COLOR` provided by frontscope code -- just don't forget to reset them +with valid colors once you have a sketch. Often this happens in the `setup()` +method. + +### 💡️ Set up the visualizer _(often used)_ + +There are two methods of your Visualizer class in which you can do +pre-computation before it enters the drawing loop. One is `presketch()`; +there's an empty placeholder version of this in `P5VisualizerTemplate`, and we +will cover its capabilities and why you might want to use it on the +[next page](visualizer-in-depth.md#set-up-for-drawing). + +The other is `setup()`, which is called immediately after the p5 drawing +canvas has been created. Here, you should set the values for all of your +additional data properties that you want them to have just before a new +visualization begins. It's important to set them all unless there are ones you +know won't change during the drawing process and for which you can rely on the +values set in the class initialization. That's because the frontscope may +re-run the set-up procedure, say when parameters change, without taking the +trouble to go all the way back to destroying and re-constructing your +visualizer object. Note this is the first chance to set graphics options and +access the drawing canvas (through the `this.sketch` property). Hence, you may +want to create color objects here, or do one-time graphics operations like +painting the background if you don't need to do that on every frame, or +setting the width of your drawing strokes if that won't be changing. + +If you implement `setup()`, always make sure to call `super.setup()`, which +includes the [`createCanvas()`](https://p5js.org/reference/#/p5/createCanvas) +call that must appear in every p5 setup function. You won't be able to perform +any drawing operation before this call. + +The `P5VisualizerTemplate` uses `setup()` to go to the beginning of the +sequence, set text alignment, and create palette colors. + +### 🔑️ Draw the visualization _(required)_ + +The `draw()` function is called on each frame, giving you a chance to draw +your visualization! Look at the examples and the p5 +[tutorials](https://p5js.org/learn/) and +[p5.js reference](https://p5js.org/reference/) to learn about what you can do. + +Drawing tools and options are found in the graphics context, `this.sketch`. +Sequence information is found in `this.seq`. This is a `SequenceInterface` +object, so it will always have a method `getElement(n)` that returns the `n`th +entry in the sequence. + +It's important to realize that while your `draw()` method is operating, the +browser can't do anything else. So it's important to limit how much +computation you do in any one call to `draw()`. That's why you're encouraged +to do animations that just change things somewhat on each frame. For example, +if you are doing a visualization that involves summary statistics on the +values of a sequence (maybe it computes their mean and standard deviation), +don't try to compute all the entries of a sequence that might conceivably have +millions of entries in a single call to `draw()`. Instead think about using an +[incremental algorithm](https://math.stackexchange.com/questions/102978) for +computing these statistics, and add a hundred or so terms on each frame, and +progressively display on each frame the summary of what you've processed so +far. You'll often find that animation tells you more about the sequence than +the single final summary would have, anyway. + +You have to implement the `draw()` method, even if it does nothing. Your +visualizer can't be loaded into Numberscope without it. The +`P5VisualizerTemplate` uses `draw()` to write the value of the current entry +in large type, make a two-color rectangle that serves as a progress bar, and +display a hint about the controls. + +### 💡️ Stop and start animation _(often used)_ + +If your visualization is a static picture (like the `P5VisualizerTemplate`) or +if `draw()` has executed enough times that you have nothing further left to +show, call the visualizer `stop()` method when you're done drawing. This +method stops the p5 animation loop, so you don't waste processor time +repeatedly drawing the same picture. + +If you have previous experience with p5.js, you might be used to calling +`noLoop()` and `loop()` to stop and start your sketch. In a Visualizer, +however, you need to use its `stop()` and `continue()` methods to ensure that +all of Numberscope's controls remain properly updated. + +### 💡️ Respond to user interactions _(often used)_ + +Each time the user interacts with your visualization, an +[event handling](https://p5js.org/reference/#Events) function like +`keyPressed()` (used by the `P5VisualizerTemplate` to move the current index +back and forth) or `mouseClicked()` will be called, giving you a chance to +respond. There are handlers for a wide variety of input events. Note that if +you previously stopped drawing by calling the `stop()` method but would like +to draw some more frames based on the event that occurred, you can call the +`continue()` method to restart the drawing loop. + +## 🔑️ How to make your visualizer available _(required)_ + +The engine expects the visualizer to be packaged in a `VisualizerExportModule` +object, constructed from the visualizer class and a short description string. +This "export" happens after the class definition. + +## How to document your visualizer + +Documentation should be included in the source file between markdown tags +`/** md` and `**/`. This will be extracted from the source code and +automatically collated into the online documentation. The p5 Template +visualizer demonstrates our documentation conventions. That template also +includes other helpful comments marked with `// ===`; these are not compiled +into the online documentation and are only provided in the template to augment +this guide (you need not imitate this). You should also provide any ordinary +code comments as you normally would. + +### Name, sample image, and description + +In a documentation block just before the visualizer class definition, put a +level-1 heading with the visualizer's name, followed by a sample image and a +description. + +To make the sample image, export an image of your visualizer with the snapshot +button in the UI, and put it in the `src/assets/img` +directory. (You can crop it with an image editing tool if you like to show the +important detail.) Add the image to the documentation page as shown in the +examples, using the `width` property for scaling. + +Below the sample image, describe what the visualizer does. Please be precise +about exactly how the image is created from the terms of the sequence. + +### Parameters + +In a documentation block just before definition of `params`, put "Parameters" +in a level-2 heading. Inside the definition of `params`, just before each the +parameter's key, put a documentation block that displays like this: + +> **Parameter name:** Description of the parameter. _(Type and constraints.)_ + +### Controls + +If users can interact with your visualizer while it's running, these +interactions should be documented too. In a documentation block just before +the first event handling method, put "Controls" in a level-2 heading. Display +control information like this: + +> **Key or gesture:** Description of effect. + +You can organize the control documentation comments in whatever way makes the +most sense. If possible, order the event handling methods so that most +controls can be documented near the method that implements them. + +### Additional documentation + +Any additional documentation should go in documentation comments after all of +the event handling methods. This is the place for in-depth discussions, +example settings, image galleries, credits, and so forth. + +Code-related documentation, like explanations of algorithms used in the +visualizer, should go as close as possible to the related code. This means, in +particular, that code requiring additional documentation should come after the +event handling methods if possible. + +### Export block + +The `VisualizerExportModule` block should be the last thing in the visualizer +source file. That makes it easy to find. + +## Where to put your visualizer + +However you made it, when a visualizer is ready for other Numberscope users, +place the file containing its class definition and export module in the folder +`src/visualizers`. When the frontscope client runs, it'll find your visualizer +and compile it at runtime. + +If running `npm run dev`, visualizers in `src/visualizers` will be available. +As discussed +[on the previous page](visualizer-overview.md#develop-your-visualizer-on-the-workbench), +visualizers that aren't ready for Numberscope users should go in +`src/visualizers-workbench`. You can load them (and the P5Template visualizer +discussed in this guide) to see how they work by running with +`npm run dev:workbench`. diff --git a/doc/visualizer-in-depth.md b/doc/visualizer-in-depth.md new file mode 100644 index 00000000..655ec101 --- /dev/null +++ b/doc/visualizer-in-depth.md @@ -0,0 +1,530 @@ +# A p5 visualizer, in detail + +As on the [previous page](visualizer-basics.md), let's begin with a (more +detailed) diagram of the life cycle of a visualizer. This version represents +all of the methods that your Visualizer class might want to implement, extend, +or override to accomplish a variety of effects, as well as all of the +significant actions that might occur as someone explores sequences with your +visualizer. + +````d2 layout="elk" +direction: down +classes: { + init: {style: { + stroke: red + font-size: 18 + font-color: darkred + } } + parChg: {style: { + stroke: orange + font-size: 18 + font-color: chocolate + } } + seqChg: {style: { + stroke: green + font-size: 18 + font-color: darkGreen + } } + resize: {style: { + stroke: purple + font-size: 18 + font-color: indigo + } } + any: {style: { + stroke: black + stroke-width: 2 + font-size: 18 + font-color: black + } } + dot: { + shape: circle + width: 10 + } + method: { + height: 40 + style: { + border-radius: 10 + font-size: 20 + } + } + returns: { + shape: cf-many + style.opacity: 0.0 + } + deflt: { + style: { + stroke-width: 3 + stroke-dash: 5 + } + } + pseudolabel: { + shape: text + style: { + italic: true + font-size: 16 + } + } +} + +secret: { + label: "" + near: center-left + style: {opacity: 0.0} + Start: "" { + class: [dot; init] + style.fill: red + } + dummy: "" { + height: 1115 + style.opacity: 0.0 + } + Start -> dummy {style.opacity: 0.0} +} + +early: { + direction: right + label: "can't use\nsequence or sketch " + label.near: top-right + style.fill: "#fee" + style.font-size: 18 + Init: |md + ### Create visualizer object + ```txt + property initializers, + constructor + ``` + | + SPC: |md + ### Parameter checks + ```txt + individual + validate() functions + ``` + | + Init -> SPC: {class: init} +} + +secret.Start -> early.Init: "init (page load/\nvisualizer change)" { + class: init + style.font-size: 20 +} + +mid: { + label: " sequence available,\nno sketch" + label.near: top-left + style.fill: "#ffe" + style.font-size: 18 + CheckPar: "checkParameters()" {class: method} + parUp: "parameter\nchange" {class: [pseudolabel; parChg]} + checkPar -- parUp {class: parChg} + ParChg: "parametersChanged()" {class: method} + parUp -> ParChg {class: parChg} + Reset: "reset()" {class: method} + checkPar --> Reset: "init" {class: init} + checkPar --> Reset: "sequence\nchange" {class: seqChg} + ParChg -> Reset {class: [deflt; parChg]} + PreSk: "presketch()" {class: method} + Reset -> PreSk: "init" {class: init} + Reset -> PreSk: "sequence\nchange" {class: seqChg} + Reset -> PreSk: "canvas\nresize" {class: resize} + routingQ: "" {class: [dot; resize]; width: 2} + routingR: "" {class: [dot; resize]; width: 2} + routingQ -- routingR -> Reset: {class: [deflt; resize]} + routingZ: "" {class: [dot; any]; width: 1} + PreSk <-- routingZ { + class: any + source-arrowhead: "in any\nevent" {shape: cf-many} + } +} +early.SPC -> mid.CheckPar: "init" {class: init} +early.SPC -> mid.CheckPar: "parameter\nchange" {class: parChg} +late: { + near: center-right + direction: up + label: "sequence & sketch available" + label.near: top-right + style.fill: "#efe" + style.font-size: 19 + Setup: "setup()" {class: method} + Draw: |md + ### p5 Draw Loop + ```txt + repeatedly calls + draw() method + for you; you can + stop()/continue() + in any method + ``` + | {style.font-size: 20} + Setup -> Draw + Draw -> Draw + routingP: "" { + class: [dot; parChg] + width: 1 + } + Draw -- routingP {class: parChg} + Resize: "resized()" {class: method} + Draw -> Resize: "canvas\nresize" {class: resize} + Resize <-> Draw { + class: resize + source-arrowhead: " (returns\nfalse)" { + shape: cf-many + } + } + Event: "" {class: dot; style.fill: blue} + Handler: "(handler method)" {class: method} + Event -> Handler: "mouse click/\nkey press/\netc." +} +spacer: "" { + width: 200 + style.opacity: 0.0 +} + +mid.routingQ -> late.Resize: "canvas\nresize" { + class: [deflt; resize] + target-arrowhead: "(returns \ntrue)" {shape: cf-many} +} +mid.Reset -> late.Setup: "parameter\nchange" {class: parChg} +mid.routingZ -> late.Setup {class: any} +late.routingP -> early.SPC: "parameter\nchange" {class: parChg} +late.Draw -> mid.CheckPar: " sequence\nchange" {class: seqChg} + +legend: { + near: top-right + label.near: top-left + direction: right + method: "classMethod()" { + class: method + style.opacity: 0.6 + } + otherCode: | + ### Other code section + ```txt + details + ... + ``` + | {style.opacity: 0.6} + method -> otherCode: "action/event" {style.font-size: 18} + method -> otherCode: "default behavior\n(can be overridden)" {class: deflt} + style.opacity: 0.4 +} +```` + +Here, we systematically review all properties and methods of P5Visualizer. +Information already on the [introductory page](visualizer-basics.md) is not +repeated here, but there are more items as well as additional detail on many +previously mentioned items. + +### Parameters + +By declaring that the `paramDesc` object you build your Visualizer class with +`satisfies GenericParamDescription`, you gain two advantages: + +1. TypeScript checks that all of the + [`ParamInterface`](../src/shared/Paramable.md) objects describing + parameters in your `paramDesc` conform to the requirements, and +2. TypeScript assigns a "narrow" type to the object literal giving the + `paramDesc` so that the corresponding properties in your Visualizer + instances will be assigned the correct realized types. + +Note that if your visualizer code internally changes one of the properties +corresponding to a parameter (for example, after the user zooms the view you +update the `magnification` value which is a parameter of your visualization), +you should immediately call the `refreshParams()` method. This method will +copy the new values back into the properties used for controlling the user +interface (UI), so that they will be visible on screen. + +### Determine validity of parameter values + +There are actually three ways the validity of user input can be checked. They +are listed here in order of preference. The first is automatic type checking, +based on the `type` property of a parameter. Type errors are announced by the +UI next to the relevant input field. + +The second consists of storing a function in the `validate` property of a +parameter in the paramDesc structure, as described in the +[introductory page](visualizer-basics.md#determine-validity-of-parameter-values-often-used). +Further detail is available [here](../src/shared/Paramable.md). Such checks +concern only that single parameter. Any errors or warning messages from these +checks will immediately be displayed to the user next to the relevant input +field. + +The third provides an opportunity to check overall consistency among multiple +parameters. For example, it may be that the number of items in two separate +list parameters must be equal. Such checks can be performed in the function +`checkParameters()`, which is called whenever parameters are changed by the +user. This method gives you a chance to check the parameter values and prompt +the user to correct any invalid ones. In this function, you can provide errors +or warning messages to be displayed at the top of the parameters panel by +placing them in the returned status object. You may also or instead put errors +or warnings next to a specific parameter: suppose the parameter is named +`speed`. Then you can write `this.statusOf.speed.addWarning(message)`, for +example. Note that if you add an error to any individual parameter in this +way, you should also make sure that `checkParameters()` returns an invalid +status. + +By the time `checkParameters()` is called, the visualizer's sequence should be +sufficiently initialized that you can depend on its `first` and `last` (index) +values, in case you need those for validation. (And hence, `checkParameters()` +will be called again if the sequence changes.) On the other hand, the sketch +will not necessarily yet be available. + +You can do sketch-dependent validation in [`setup()`](#set-up-for-drawing), as +described below. By that point (any time in the life cycle beyond +`checkParameters()`), errors you add to the parameters or the visualizer's +`validationStatus` won't block the new parameters from taking effect as they +would if handled in `checkParameters()`, but at least the person running the +visualization will be notified in the UI. Another fine point in this case is +that since `validationStatus` and `statusOf` properties are not by default +reset when `setup()` is again called, you may need to be careful that +additional copies of your error or warning do not accumulate as `setup()` is +repeatedly called (say for the sake of resizing the screen). + +Finally, note that `checkParameters()` is called with _tentative_ parameter +values, and there is no guarantee that those parameter values will actually be +loaded into the visualizer. So (a) only access the values through the +passed-in `params` object, not through the visualizer itself, and (b) don't +start setting up for visualization using those values: leave that to one of +the set-up functions discussed [below](#set-up-for-drawing). + +### Name and description + +The [introductory page](visualizer-basics.md#name-required-and-description) +notes to put the name in a static `category` property. This guideline arises +from a convention that Visualizer instances do not have individual names, but +instead that their `name` properties are the same for all instances of the +same class. But there still _is_ a `name` property; the base class simply +fills it in from the `category`, and makes it read-only. + +### Other properties + +#### Vue and reactive objects + +It is important to know that Vue will instrument (i.e., insert code into) your +Visualizer class to be a "reactive" object so that changes to its properties +can trigger changes to what's displayed in the browser window. This +instrumenting is necessary to support the two-way interaction between +parameter controls in the UI and corresponding properties of your visualizer. +However, the instrumentation can on occasion be a nuisance, causing (for +example) update of internal state properties (that may have nothing to do with +the browser display) to become extremely expensive or even behave erroneously. +Primitive data fields (numbers, strings, bigints, and so on) are not +susceptible to this problem, but object data (Arrays, Maps, plain objects, and +the like) can very much be. If you suspect that some object in your internal +state may be falling into this trap, you are advised to call the Vue +`markRaw()` function on it before you store it in a data property defined in +your class. That call tells Vue not to instrument its argument object at all. +It means that changes to that object cannot directly affect the browser view; +but presumably you are already calling `refreshParams()` to explicitly update +the view when needed, so there is unlikely to be a problem. Note, however, +that you should not call `markRaw()` on the values of _parameters_ even if +they are objects, as in the case of `ParamType.NUMBER_ARRAY`; doing so could +prevent proper display updating. + +#### p5 color values + +Many visualizers want to manipulate and store p5.Color objects in data +properties. TypeScript throws a bit of a hitch into this process. If you +declare a property of your Visualizer class to have type `p5.Color`, it will +naturally insist that you initialize that property, either on the line in +which you declare it, or in the class constructor function. However, the p5 +system is (as of this writing) designed so that there is no way to construct a +p5.Color object without access to the p5 sketch in which the color is to be +used. But at Visualizer construction time, there is no p5 sketch available -- +it doesn't exist yet. + +As a workaround to this impasse, `src/visualizers/P5Visualizer.ts` exports +`INVALID_COLOR`, a value forcibly typed to `p5.Color` that you can use to +initialize your p5.Color properties. Then in the `setup()` method, you can +fill in these properties with the actual colors you want them to represent. +Make sure you do fill them all in -- if you attempt to draw with an +`INVALID_COLOR`, p5 will crash. + +### React to parameter changes + +Any time a parameter value is assigned into its corresponding property in your +Visualizer object, the `parametersChanged()` method of your Visualizer is +called with a list of the parameter name(s) that changed. The default +implementation of this method in the P5Visualizer base class resets the +display of the visualizer so that it re-runs from the beginning. So you likely +do not need to modify that behavior. However, there could be a specialized +situation in which you might want to: imagine you are progressively drawing a +diagram and would like to simply continue in the middle of drawing with a +newly-specified color when its parameter changes (rather than re-drawing the +entire thing from the beginning with that new color). In that case, you could +implement an altered `parametersChanged()` that would detect the situation and +avoid the reset. + +### Inhabit a page element + +Each time the visualizer is inserted into a page element, the `inhabit()` +function is called, giving you access to the element the visualizer is about +to inhabit. If you don't need information from the web page your visualizer is +running in, or access to its document object model (DOM), you should be able +to just inherit the base class `inherit()` and not implement it in your +Visualizer class. The full details on this method are in the +[visualizer interface](../src/visualizers/VisualizerInterface.md) +documentation. + +### Handle aspect ratio + +By default, a visualizer does not set its own sketch dimensions (width and +height). Instead, the Numberscope UI assigns these dimensions, providing the +visualizer with the largest available canvas for the user's setup. After +`setup()` is run, the dimensions are stored in `this.sketch.width` and +`this.sketch.height` and the visualizer should handle all its drawing with +respect to these. + +In some cases, a visualizer design may run best in a fixed aspect ratio, such +as a square canvas. In this case, you can request a specific aspect ratio by +implementing `requestedAspectRatio()` to return the desired ratio as a number +representing width/height. In this case, the UI will provide a canvas of the +maximal size with that ratio. + +### Set up for drawing + +The [introductory page](visualizer-basics.md#set-up-the-visualizer-often-used) +covered the `setup()` method of a P5Visualizer that is called once when a new +graphics context `this.sketch` becomes available. However, there is an earlier +opportunity to do pre-computation as well. That is the `presketch()` method, +which runs asynchronously, meaning that the browser will not be blocked while +this function completes. This facility is not a part of p5.js, but a part of +the P5Visualizer design. The `presketch()` method is called by the framework +with one argument, representing the size of the canvas to be created as a +ViewSize object with number fields `width` and `height`. + +If you implement `presketch()`, begin by calling +`await super.presketch(size)`, which will initialize the sequence that the +visualizer is viewing. After this call, you have access to the values of the +sequence, so you can do sequence-dependent initialization here. It is OK to +set up internal data variables in this method. For example, this is a good +place to populate an array with time-consuming precomputed values you will use +repeatedly during the sketch. However, in `presketch()` you still have no +access to the p5 canvas or the `this.sketch` object. + +Note also that `presketch()` is called when there is a new visualizer, when +the sequence changes, when the canvas size changes, and when you reload the +page or visit a new Numberscope URL. It is not called when visualizer +parameters change. So if there is initialization you want to do only on these +more signifcant changes but not on parameter changes, then `presketch()` is a +good method. + +When a visualizer is resized, or the restart button on Numberscope is pressed, +the class function `reset()` is called. By default, a new canvas is created on +a `reset()` (and so `setup()` will be called). In this event, `presketch()` +will only be called if that new canvas is a different size than the previous +one. However, the visualizer object constructor is not re-run and any data +stored in variables in the visualizer object persists. Those default behaviors +mean that you have the option to forgo re-doing expensive pre-computations: if +they don't need sketch access, you can put such calculations in `presketch()`. +But the flip side is that you can't rely on property initializers, your class +constructor, or on (say) `checkParameters()` to put your other visualizer +instance properties in a "clean" state. + +For example, if you need some array to be all zeros when you start drawing +your visualization, and previous calls to `draw()` may have changed some of +those array entries, you need to zero it out in `setup()` because after a +parameter change, that's the only preparation function that will by default be +called before going back into the drawing loop. + +For even greater customization of what happens when, you can override/extend +`reset()` from the `P5Visualizer` base class, or you can define a `resized()` +method for behavior that only occurs when the canvas is resized. Note that in +this latter case, you can control whether the framework does the `reset()` for +you: return `true` if you have handled any need to reset (and so the framework +should NOT call `reset()`), and false if you do want the framework to +`reset()`. + +### Draw your visualization + +The P5Visualizer provides a utility method `hatchRect(x, y, w, h)` that draws +a rectangle with corner at (x, y) and width w and height h, filled with +diagonal hatch lines. + +### Show or stop the visualization; depart from a page element + +You shouldn't frequently need to implement `show()`, `stop()`, or `depart()`. +Briefly, `show()` begins the drawing loop for the first time, `stop()` halts +or limits the duration of the drawing loop, and `depart()` removes the +visualization from displaying in the browser and cleans up any resources it +might be using. You can learn more about them from the +[visualizer interface](../src/visualizers/VisualizerInterface.md) +documentation, and from how they're implemented in the `P5Visualizer` base +class. + +### Draw the visualization + +Note that accessing `this.sketch` triggers some consistency checks before the +actual sketch object can be returned. Hence, to eke out the maximum +performance in your `draw()` function (if you find that you are +computationally bound in the frame rate you can achieve), we recommend storing +its value as a local constant before the first time you need it in `draw()`. +This tactic avoids redundant checks. + +### Stop and start animation + +The +[introductory page](visualizer-basics.md#stop-and-start-animation-often-used) +points out that you can call `continue()` from an event-handling function to +ensure that the visualization will be redrawn. Actually, you can call +`continue()` from anywhere in your code that the sketch is available (see the +lifecycle diagram above) and it will have a similar effect. This mechanism is +most often useful in event handlers, but could be helpful elsewhere. Moreover, +suppose when you call continue you know there are a limited number of frames +you will need to draw, but there isn't otherwise logic in your `draw()` +function to `stop()` again when those are done. In that case, you can +immediately call `stop(n)` after the `continue()` and only _n_ more frames +will be drawn, after which the visualizer will automatically stop drawing. + +Again, you can call this form of `stop()` with an argument anywhere the sketch +is available. So if you know you only want your visualization to run for 100 +frames, you could just call `this.stop(100)` right in your `setup()` function. + +### Respond to user interactions + +Here is a list of all of the event handling methods that you may implement in +your Visualizer. Each is called when the corresponding event occurs on the +browser, often with the JavaScript `Event` structure as shown. + +``` +{! ../src/visualizers/P5Visualizer.ts extract: + start: class.WithP5 + stop: setup + replace: ['\s*([^{}]*)'] +!} +``` + +### How to report errors + +There are three ways to let people interacting with a visualizer know that +something unexpected has happened. The first has been mentioned above: at any +time you can add errors or warnings to the `validationStatus` property or the +values of the `statusOf[PARAMETER_NAME]` property. These will be reflected in +the appropriate place in the parameter tab for the Visualizer. + +The second is with the [alertMessage](../src/shared/alertMessage.md) utility. +And the third is just to throw an error. If it's not caught anywhere else, the +visualizer framework will show it in an error dialog. + +### Getting random numbers + +Do not use the built-in `Math.random()` random number generator, because there +is no practical mechanism for making its output reproducible for testing +purposes. Instead, obtain random numbers using the provided `math` module, +along the following lines: + +``` +import {math} from '@/shared/math' +import {P5Visualizer} from '@/visualizers/P5Visualizer' + +class DiceVisualizer extends P5Visualizer({}) { + ... + draw() { + ... + // Roll a die (generate a number 1, 2, 3, 4, 5, or 6, + // all equally likely, at random): + const myRoll = math.randomInt(1, 7) // upper limit is exclusive. + ... +``` + +For more details, see the [math documentation](../src/shared/math.md). diff --git a/doc/visualizer-overview.md b/doc/visualizer-overview.md new file mode 100644 index 00000000..2f054232 --- /dev/null +++ b/doc/visualizer-overview.md @@ -0,0 +1,63 @@ +# Overview + +Here's a general outline of creating visualizers that we will flesh out more +in the following pages. + +### Grab a template for your chosen graphics framework + +The easiest way to build a visualizer is to extend a pre-made visualizer base +class, which automatically sets up a graphics framework for you to use. Right +now, there are only two base classes available: + +- [`P5Visualizer`](visualizer-basics.md) uses the + [**p5.js**](https://p5js.org) library for graphics and user interaction. +- `P5GLVisualizer`: a slight variant of `P5Visualizer`. You should derive + from this base class if you wish to use p5.js in WebGL mode. See the + [Turtle](../src/visualizers/Turtle.md) visualizer for an example. + +Note that by "extend a base class," we mean that in the standard TypeScript +sense: You will see that visualizer source code files literally contain a line +like + +`class Differences extends P5Visualizer(paramDesc) {` + +followed by the code that implements the visualizer class. For a quick start, +copy and modify the template file for your chosen framework, which you can +find in `src/visualizers-workbench`. + +If you want to use a new graphics framework, you'll need to write your own +implementation of the +[visualizer interface](../src/visualizers/VisualizerInterface.md). + +### Document as you write + +Each visualizer has its user guide documentation woven into the source code, +using special `/** md` … `**/` comments that are automatically +compiled into a [documentation page](../src/visualizers/Differences.md). We +have some [conventions](visualizer-basics.md#how-to-document-your-visualizer) +for what the documentation comments should include and where in the source +code they should go. + +### Develop your visualizer on the workbench + +While you're working on a visualizer, we recommend keeping it in the +`src/visualizers-workbench` directory, where frontscope typically won't notice +it. To load it, run frontscope in "workbench mode" by calling +`npm run dev:workbench`. + +When you're ready to propose your visualizer as an official part of +Numberscope, you'll move it to `src/visualizers`. Then frontscope will notice +and load it when you call the usual `npm run dev`. You can find some more +detail on this process on the +[next page](visualizer-basics.md#where-to-put-your-visualizer). + +A visualizer, like a work of art, is never really finished—even when it's +accepted into Numberscope. Even the humble template visualizer discussed below +could be extended. You could shorten the infinite progress bar for finite +sequences, allow fast navigation by holding down arrow keys, add a progress +bar mouse-over that shows the index… the possibilities are endless. We invite +you to try extending or enhancing existing visualizers as well as building +your own. + +You can proceed to the [next page](visualizer-basics.md) for a tour of the +core implementation parts of a basic visualizer. diff --git a/doc/working-with-bigints.md b/doc/working-with-bigints.md index 3f68e355..bf48d164 100644 --- a/doc/working-with-bigints.md +++ b/doc/working-with-bigints.md @@ -15,24 +15,33 @@ code: - The name of the TypeScript type for bigints is `bigint`. However, when you want to make a value `v` of some other type into a bigint, you call `BigInt(v)`. -- Literal values of type `bigint` consist digits followed by a lower-case n, - like 73n for the bigint with value 73, or 0n or -10n, etc. -- Right now unfortunately mathjs does not work on bigints but we are working - on that. +- Literal values of type `bigint` consist of digits followed by a lower-case + n, like 73n for the bigint with value 73, or 0n or -10n, etc. +- Right now unfortunately mathjs does not work on bigints but soon we will + update to a version that does. - In the meantime to compensate for the lack of bigints in mathjs, we have a - module (`src/shared/math`) in numberscope to supply some math utilities. - You should familiarize yourself with the - [functions it offers](../src/shared/math.md). + code file (`@/shared/math`) in numberscope that adds some utilities to the + `math` module for working with bigints. You should familiarize yourself + with the [functions it offers](../src/shared/math.md). - In particular, if you are forced to convert a bigint to the JavaScript - number type, you should do it with the `safeNumber` function provided by - the math utility module, unless for some structural reason you are + number type, you should do it with the `math.safeNumber` function provided + by the math utility module, unless for some structural reason you are **certain** the bigint won't overflow the number. That will provide overflow checking and throw an error if accuracy would be lost. Note that any value you get from the OEIS may be too large to fit in the JavaScript number type. -- In any math operations, both operands must be of the same type. So - generally prefer converting to bigints any numbers you may need to use in - a calculation with bigints, if possible. +- For example, you may want to evaluate a function like sine or log on a + bigint value. For sine, you will have to convert to a number; for log we + have `natlog` in the math module that works on both numbers and bigints. + Note that if you have a bigint larger than can safely be converted to a + number (e.g., larger than JavaScript `Number.MAX_SAFE_INTEGER`), and you + want to take say the sine of it, it should be possible to use double-angle + and sum formulas to bring the operand down into the safe range for + conversion. If there is demand for such operations, we could implement + them in the math module. +- In any arithmetic operations like + and \*, both operands must be of the + same type. So generally prefer converting to bigints any numbers you may + need to use in a calculation with bigints, if possible. - However, note that the result of dividing one bigint by another is automatically "floored" to produce an integer result. Be careful to think if that's what you want; if not, you may need to rearrange your diff --git a/doc/working-with-git-and-github.md b/doc/working-with-git-and-github.md index 56ccb0cc..9f475588 100644 --- a/doc/working-with-git-and-github.md +++ b/doc/working-with-git-and-github.md @@ -1,91 +1,354 @@ # Working with Git and GitHub -Git and GitHub are separate tools, but since there is some overlap when it -comes to working with them, we describe the Git and GitHub operations relevant -to Numberscope in the same document. +Git and GitHub are separate tools: [Git](https://git-scm.com/) is a software +package for tracking versions of files, often used for source code management, +and [GitHub](https://github.com) is a commercial website that houses +"repositories" of versioned files relating to numerous different projects, +including Numberscope. Since there is some overlap when it comes to working +with them, though, we describe the Git and GitHub operations relevant to +Numberscope in this same document. Contents - [Basic operations](#basic-operations) + - [Clone a repo](#clone-a-repo) + - [See what the current git situation is](#display-status) + - [Create a branch](#create-a-branch) - [Add changes](#add-changes) - [Commit changes](#commit-changes) + - [Create a fork](#create-a-fork) + - [Add a remote](#add-a-remote) + - [Push a branch](#push-a-branch) - [Push changes](#push-changes) - [Submit a pull request](#submit-a-pull-request) - [Advanced operations](#advanced-operations) - [Stash your changes](#stash-your-changes) - [Unstash your changes](#unstash-your-changes) - - [Create a branch](#create-a-branch) - - [Push a branch](#push-a-branch) - - [Create a fork](#create-a-fork) - - [Clone a repo](#clone-a-repo) - - [Add a remote](#add-a-remote) - - [Sync local fork with remote original](#sync-local-fork-with-remote-original) + - [Check your remotes](#check-your-remotes) + - [Sync your local clone](#sync-a-local-clone) + - [Rebase your branch](#rebase-your-branch) ## Basic operations -When using Git, it can be helpful to think of it as photographer who stages +When using Git, it can be helpful to think of it as a photographer who stages your work ("adds" changes), takes a snapshot of it ("commits" changes), and stores the photo somewhere safe ("pushes" changes to e.g. GitHub). +### Clone a repo + +As described in the +[onboarding](onboarding.md/#setting-up-your-computer-for-development) page, to +run the frontscope user interface from its source code and/or to set up for +making and then submitting changes to the code, you need a copy of all the +code (and the previous snapshots of it as it developed) on your computer. +That's called "cloning" the repository. + +To do this, make sure you are in a directory on your computer where you are +comfortable with new subdirectories being created for the project(s) you +clone. (Of course, you must also have the git software installed.) Then issue +a command that looks like this: + +```sh +git clone [URI_of_repository] +``` + +Note that `[URI_of_repository]` is a placeholder for information that you need +to fill in, not something you type as is. In general, we will put placeholders +like this in brackets `[]` so you know what to fill in. + +How do you find the URI of the repository you want? In the case it is hosted +on GitHub, as [frontscope](https://github.com/numberscope/frontscope) is, you +navigate to the repository page (the one just linked). In the center of the +page left-to-right and near the top of the page, you will see a bright green +"Code" button. Click on that button, and it will give you a choice of two +different URIs to clone the repository with: + +1. via HTTPS: This is an unauthenticated connection to the GitHub server, so + it is easiest to use for download. However, you cannot submit code changes + via an HTTPS URI. That's not a problem for the standard repository for + frontscope, because you never submit changes directly to the standard + repository. Instead, you will create what's called a "fork" and submit your + changes through the fork. So bottom line, we recommend you clone frontscope + via its HTTPS URI, which is + `https://github.com/numberscope/frontscope.git`. +2. via SSH: This method of connecting is authenticated and will allow you to + upload changes to GitHub. It requires + [some setup](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account). + If you have created a clone of frontscope for the purpose of submitting + code, you will have to connect to it via an SSH URI, which has the format + `git@github.com:[USER]/[REPO].git`. + +The "Code" button will also show you a command you could use to clone the +repository via the "GitHub Command-Line Interface" (or "GitHub CLI"). That +method requires installing additional software. If you're comfortable with the +[GitHub CLI](https://cli.github.com/), you can use it for several of the tasks +on this page, but we won't be going into the details of it here. + +To sum this all up for the case of frontscope, we recommend you clone it via +`git clone https://github.com/numberscope/frontscope.git`. + +### Display status + +One of the main uses of Git is to facilitate different people working together +by creating (as it were) different albums of photos of your work (called +"branches"). The "official" version of +[frontscope](https://github.com/numberscope/frontscope) (or +[backscope](https://github.com/numberscope/backscope)) is in the branch called +"main" in its standard repository (as just linked to). But as we will see, you +can create new branches for your personal project that involves modifying the +source code, to make sure that your work doesn't interfere with anyone else's. + +But first, you will often need to see what the situation is with your copy of +frontscope. Let's assume from now on that you have cloned a repository and +your current directory is within the "working directory" of that clone. (In +other words, if you have just cloned frontscope, say, then you would need to + +`cd frontscope` + +before executing any of the commands in the rest of this page.) + +OK, so if you are in the working directory of your clone and you want to see +what the situation is, execute + +`git status` + +This command will tell you what "branch" of the repository you are currently +working in, and it will let you know about any differences between the files +in that working directory and the last snapshot, i.e. "commit", of the +project. + +For example, if you execute `git status` immediately after cloning the +frontscope repository, you will see: + +```text +On branch main +Your branch is up to date with 'origin/main'. + +nothing to commit, working tree clean +``` + +On the other hand, if you had switched to a different branch (more about how +to do that in a moment), modified one file and created one new one, you might +see something like this: + +```text +On branch git_docs +Changes not staged for commit: + (use "git add ..." to update what will be committed) + (use "git restore ..." to discard changes in working directory) + modified: doc/working-with-git-and-github.md + +Untracked files: + (use "git add ..." to include in what will be committed) + doc/example-documentation.md + +no changes added to commit (use "git add" and/or "git commit -a") +``` + +Note that we have used a different branch in this example because you should +_never make changes directly to main_. Always create and work in a different +branch. + +### Create a branch + +To create a branch and switch to it in one command, first make sure (for +example, using `git status`) that you are "on" the branch that you want your +new branch based on. That's sort of the "parent" of the new branch -- in other +words, the baseline that your branch will record a series of changes to. +Typically for working on Numberscope, that base will be the `main` branch, so +make sure that's checked out. Then issue the following command: + +```sh +git checkout -b [your_new_branch_name] +``` + +(At Numberscope we use `snake_case` for branch names.) Once you've done this, +you have your own "little world" where you can make changes without affecting +anyone else. Go ahead and modify files or add new ones to get the +visualization you want! + ### Add changes -To add or "stage" your work for a commit or "snapshot", issue the following +At some point you will have something new and/or improved working, and you +will want to get ready to take a snapshot (i.e. make a "commit" to your +branch) to possibly share or submit to Numberscope, or just as a checkpoint of +what you have done so far. + +For this, you must set the stage for this commit. To "add" one file you have +been working on to the commit that's about to be made, issue the following command: ```sh -git add /path/to/file/here +git add [path/to/file] ``` -(You should do this command for each file you need to stage.) +Note that the paths are _relative_ to the top-level directory of the working +tree, i.e. to the directory named `frontscope` in our case. For example, the +path of this documentation file is `doc/working-with-git-and-github.md`. + +You need to do this for each file you want to stage for this commit; or, see +the next section for a faster option you can sometimes use. ### Commit changes -To commit your work or take a "snapshot" of the work, issue the following -command: +To take a snapshot of the changed files you have added (i.e, "commit" them), +issue the following command: ```sh -git commit -m "put your commit message here" +git commit -m "[put your commit message here]" ``` +As a shortcut, you can add and commit all of the changed files in your working +tree with + +```sh +git commit -a -m "[put your commit message here]" +``` + +Note that this latter command does **not** include any new ("untracked") +files. Those you must explicitly add with the `git add` command. That's to +make sure that you don't inadvertently put some temporary junk file you may +have made into the repository. + You should write your commit messages according to the "semantic commit message" [guidelines](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716). -### Push changes +If your changes are more extensive than can be comfortably described in a +single sentence, leave off the `-m "[message]"` part of the command. Then an +editor will pop up where you can insert the description of your changes, Start +with one summary line in the semantic commit message format, then skip a line, +and go into more detail in paragraph(s) below. When you save the message file, +the commit will complete. In these descriptions, always describe the commit in +present tense: "This commit implements a frobozzinator", not "This commit +implemented a frobozzinator". Having them in a uniform format makes the +eventual git log of changes much easier to read. + +Note that if you are working on frontscope and you have installed the standard +pre-commit actions (as will happen automatically if you execute the usual +`npm install` when you start working with your clone), then before the commit +actually occurs, git will run several +[code integrity checks](./husky-pre-commit.md). If any of the checks fail, +creating the commit will be blocked until you have further modified the code +so that they pass. Making these fixes may be very simple: for example, there +is a code formatting check, and executing `npm run lint` to run Numberscope's +standard "prettyprinting" utility may well resolve any problems it is +reporting. Or the issue may be more involved: one of the unit tests or +end-to-end tests may have uncovered a bug you've inadvertently introduced. See +the page on [code tests](./code-tests.md) for more information. + +### Create a fork + +Now that you have a new snapshot of your work, you might like a safe place to +put it, and/or you may want to submit what you've done for consideration to be +incorporated into the official Numberscope (that's called "making a pull +request"). Either way, you'll need to create a "fork" of the frontscope +repository, since you are not allowed to submit directly to the official +repository. + +Creating a fork of a repository is like creating your own personal copy of the +entire repository, including all present and past snapshots of the code. It's +actually essentially the same thing as cloning the repository, except that the +clone is housed on GitHub's servers rather than on your own local machine. It +also adds functionality that makes it easier to keep your fork synced with the +original and submit pull requests. + +Note: This is a GitHub operation, not a Git operation. So the first thing you +will need is a [GitHub account](https://github.com/signup). Once you have that +and are logged in: + +1. Go to the page of the repository you want to fork. For instance, here's a + link to the + [numberscope/frontscope repository](https://github.com/numberscope/frontscope). +2. In the upper right corner of the page (as of this writing) there should be + a button that says "Fork". Click that button. +3. Follow the instructions that GitHub provides. Make your GitHub account the + owner of the fork. + +### Add a remote + +Now back on your local machine, you'll want your clone to be able to +communicate not just with the standard Numberscope repository from which it +was cloned, but also with your new fork. That's called "adding a remote". +First, you'll need to grab the **SSH** URI for your fork from its page on +GitHub, see the [description of URIs](#clone-a-repo) in the section on cloning +a repository. Then execute the following command -Before you are able to do this, you either need to install the +```sh +git remote add [name_of_remote] [SSH URI for remote] +``` + +You can pick whatever name you want to use for this remote; we typically use +the name "fork" for the fork that we will primarily be submitting ("pushing") +commits to. So if your user name on GitHub is "somebody", your command might +look like + +```sh +git remote add fork git@github.com:somebody/frontscope.git +``` + +### Push a branch + +Now you're finally ready to upload your snapshot, or in git lingo, "push your +branch". Before you are able to do this, you either need to install the [GitHub command line interface (CLI)](https://cli.github.com/) and log in using the GitHub CLI or [install an SSH key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account). -To push your changes to a remote version of your repository (i.e. to the -version on GitHub) or to "store" your photo, issue the following command: +Presuming that you have not previously pushed anything from your current +branch to your fork, you will want to create a corresponding branch in that +remote git repository and set your local branch to "track" or correspond to +that remote branch. You do this with the following command, that also pushes +all of the changes on the current branch to the remote: + +```sh +git push -u [name_of_remote] [name_of_branch] +``` + +So for example, when first pushing the branch with the changes to this guide, +the command used was `git push -u fork git_docs`. + +### Push changes + +If you have already pushed a given branch and set it track the remote branch +as in the last section, then when you have made further commit(s) that you +want to upload, just execute: ```sh git push ``` +Git remembers the remote and branch you want to push to (assuming you used the +`-u` option earlier) and also what commit(s) were uploaded before, and only +pushes the new ones. + ### Submit a pull request Once you have a branch in working order that implements one new feature or fixes one bug or otherwise changes Numberscope in a coherent way, and that branch is pushed to your fork in the state you want to propose for inclusion in Numberscope, the first thing to do is to carefully go through the -[pull request checklist](pull-request-checklist.md). - -Presuming that you have satisfied all of the guidelines in that file, it's -time to submit a pull request (PR). To do so, go to your fork on the GitHub -website, and select your branch in the dropdown list on near the top left. -Then in the bar just below that dropdown list, you should see information on -how your branch compares with the current "main" branch of Numberscope, in -terms of the number of commits it is "ahead" of main (i.e., has added since -the last common commit to main) and the number of commits "behind" main (i.e., -the number of commits added in main since the last common commit). Ideally, -your branch is not behind main at all; otherwise, maybe some of those commits -in main might affect the changes you were working on. So if you see any -commits behind main, you might want to go back to your working copy and -[sync your local fork with remote original](#sync-local-fork-with-remote-original) -and rebase your branch on the current version of main. +[pull request checklist](pull-request-checklist.md). This is a GitHub +functionality, not one intrinsic to git. + +Presuming that you have satisfied all of the guidelines in the checklist file, +it's time to submit a "pull request" (PR). To do so, go to your fork on the +GitHub website, and select your branch in the dropdown list on near the top +left. Then in the bar just below that dropdown list, you should see +information on how your branch compares with the current "main" branch of +Numberscope, in terms of the number of commits it is "ahead" of main (i.e., +has added since the last common commit to main) and the number of commits +"behind" main (i.e., the number of commits added in main since the last common +commit). Ideally, your branch is not behind main at all; otherwise, maybe some +of those commits in main might affect the changes you were working on. So if +you see any commits behind main, you might want to go back to your working +copy and [sync your clone with the original](#sync-a-local-clone) and then +rebase your branch on the current version of main. If you submit a PR that is +not based on the current version of the main branch, you run the risk that it +may have "merge conflicts" in which both you and a newer version of main have +modified the same or nearby sections of code. Such merge conflicts will +prevent your PR from being accepted until the are resolved by rebasing on the +latest version of main. In any case, when you are satisfied your branch is ready to submit as a PR, you will notice that report of the number of commits ahead or behind main is @@ -98,7 +361,7 @@ When all of those changes look right, click the big green button at the top right that says "Create Pull Request". This click will take you to the "Open a Pull Request" form. (Note that the first time you push a branch to git, the response message actually includes a quick direct link to this form; you can -save that away and use it later, instead of navigating through GitHub's web +save that link and use it later, instead of navigating through GitHub's web interface.) There are just two boxes you need to fill out on the form to open a pull @@ -117,7 +380,7 @@ are good to go. If not, replace that placeholder with a more detailed but still brief explanation of the reason, purpose, and content of your PR. If it takes care of any of the outstanding issues concerning Numberscope listed on the GitHub site for Numberscope, include at the bottom on a separate line the -statement `Resolves #999.` (with the actual issue number in place of 999). +statement `Resolves #[issue_number].` When you are happy with the title, description, and changes made in your PR, and you agree with the statement that was pre-filled in the description (which @@ -138,8 +401,15 @@ your ideas. Thank you for submitting your work to Numberscope! ### Stash your changes -To "stash" your changes (i.e. squirrel them away for later use), issue the -following command: +You may be in the middle of working on a project, and decide that you need to +check out main again to see how something worked originally, or to start on a +new more urgent project, or something like that. But you may have changed some +of the files in the working tree, in a way that you want to save, but that is +not quite ready to turn into a bona fide commit. + +In that situation, git has a facility to squirrel away the changes you have +made so far for later use -- it's called "stashing" the changes. Use the +command: ```sh git stash @@ -153,131 +423,184 @@ To unstash your changes, issue the following command: git stash apply ``` -The above command keeps the changes in your stash. +This command recreates the changes that were previously stashed in your +working tree, and it also keeps a record of those changes in the stash. -Alternatively, if you wish to discard those changes from the stash, issue the +Alternatively, if you want to recreate the stashed changes but also +simultaneously discard them from your stack of stashed changes, issue the following command: ```sh git stash pop ``` -### Create a branch - -To create a branch and switch to it in one command, issue the following -command: +Finally, if you just want to discard a set of stashed changes _without_ +affecting your working tree, the command is: ```sh -git checkout -b your_branch_name_here +git stash drop ``` -At Numberscope we use `snake_case` for branch names. +Be careful -- the difference between "pop" and "drop" is a bit subtle. Both +get rid of the most recent set of changes in your stash, but "pop" affects +working tree whereas "drop" throws them away without affecting enything. -### Push a branch +### Check your remotes -To create a branch in a remote Git repository and push your changes to that -branch, issue the following command: +To check to see what your "remotes" (other Git repositories you are set up +communicate with via push/pull) are, issue the command below. ```sh -git push -u name_of_remote name_of_branch +$ git remote -v ``` -The name of the remote is most likely `origin` if you are pushing to a fork of -one of the Numberscope repositories. +This will show a list that might look something like -### Create a fork +```text +fork git@github.com:somebody/frontscope.git (fetch) +fork git@github.com:somebody/frontscope.git (push) +origin https://github.com/numberscope/frontscope.git (fetch) +origin https://github.com/numberscope/frontscope.git (push) +``` -Creating a fork of a repository is like creating your own personal copy of -that repository. It's basically the same thing as cloning the repository, but -it creates a GitHub repository associated with your account that mirrors the -original, at least until you start making changes to your fork. It also adds -some neat functionality that makes it easier to sync your fork with the -original and submit pull requests. +The repository you first cloned from will always be named `origin`; any other +remotes shown will have the names you assigned them when they were addes. The +name `fork` is just a typical choice for the remote you are generally going to +push to. -Note: This is a GitHub operation, not a Git operation. +### Sync a local clone -1. Go to the page of the repository you want to fork. For instance, if you - want to fork the numberscope/frontscope repository, go to - https://github.com/numberscope/frontscope. -2. In the upper right corner of the page (as of this writing) there should be - a button that says "Fork". Click that button. -3. Follow the instructions that GitHub provides. Make your GitHub account the - owner of the fork. +Once you or other people have submitted changes and they have been accepted +and "merged" into the official `main` branch, the local clone you made on your +machine will become out of date in terms of its view of the code in the main +branch. (It won't change its concept of what code is in main until you +explicitly tell it to download or "pull" the latest commits in main.) -### Clone a repo +This can be a problem -- you may not want to base a new branch on out-of-date +code, or a PR you may have submitted may have ended up in a conflicted state +because it was based on an older version of `main`. Here's how you correct the +situation. -To clone a Git repository to your computer, issue the following command: +First, you need to get back onto the main branch in your local working tree. +If you have any uncommited changes (they would show via +[`git status`](#display-status)) that you want to save, you should first +[stash](#stash-your-changes) them. Then execute ```sh -git clone https://github.com/some_user_or_org/some_repo.git +git checkout main +git pull ``` -GitHub allows you to clone the repo a few different ways: - -1. via HTTPS (easiest method, doesn't require setup, has limited - functionality, you'll eventually need the - [GitHub CLI](https://cli.github.com/)) -2. [via SSH](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account) - (hardest method, requires setup, but ultimately very convenient) - -### Add a remote - -When you use the operation `git push`, you are pushing your local commit(s) to -a remote version of your repository. - -First, [check to see what remotes you have](#check-your-remotes). - -To add a remote version of your repository, issue the following command: +This will download all of the changes to the standard `main` branch since you +created your clone or last pulled `main`, and leave your working tree on the +main branch with all of the new code. Now you if you want to return to the +branch you were on, you can execute ```sh -git remote add name_of_remote https://someurl.com/somerepo.git +git checkout [branch-name] ``` -### Check your remotes - -To check to see what your "remotes" (where you'll be pushing to) are, issue -the command below. (The items in <> brackets are placeholders. You shouldn't -actually have the <> brackets in the real thing.) +And if you previously stashed any changes, you may want to +[unstash them](#unstash-your-changes). + +### Rebase your branch + +A branch works from a specific commit in its parent branch (usually `main`) -- +its like a logbook of all of the changes you've made from that particular +snapshot of all of the code. Often, for a PR to succeed, the branch must be +based on the latest commit in `main`. So here's what you do if your branch is +based on an earlier commit in `main` (likely the one that was current when you +started your work), causing a problem with your PR. You may also want to +update your branch in this way to make sure it will work with new features +that have been introduced in main, or just to minimize the obstacles that a PR +based on it will encounter in the review process. + +First, make sure you have synced your clone, as outlined in the previous +section. Then switch to your branch (with `git checkout [branch_name]`) if +necessary. Make sure that there are no uncommitted changes (by +[stashing them](#stash-your-changes) or waiting to +[unstash them](#unstash-your-changes) if you had previously stashed them). + +Now you can instruct git to "replay" each of the changes you made in getting +your branch to where it was, creating new commits along the way. It's as if it +automatically re-edits each file in just the way you did when you were +creating your branch, except starting from the current versions of the file. +The command is: ```sh -$ git remote -v -origin git@github.com:/.git (fetch) -origin git@github.com:/.git (push) -upstream git@github.com:/.git (fetch) -upstream git@github.com:/.git (push) +git rebase main ``` -The names "origin" and "upstream" are arbitrary. That being said, they are the -traditional names. "origin" should be your fork, and "upstream" should be the -original repository. - -### Sync local fork with remote original - -See [this SO answer](https://stackoverflow.com/a/7244456). - -First, [check your remotes](#check-your-remotes). You should have something -like what was described in the [check your remotes](#check-your-remotes) -section of this doc. If not, set origin and/or upstream: - -```sh -git remote add git@github.com:/.git +This may go smoothly and simply report "Successfully rebased and updated +[branch_name]." Or it may hit a "conflict." This conflict can occur when you +changed a region of a file, and the same or very nearby region of the same +file has changed in the main branch between when you started and now. Under +these circumstances, git may not be able to reconstruct how to "re-do" the +edits you made, starting from the current version of the file. (For example, +maybe you edited the code for a function that has since been removed, renamed, +or relocated from the file where it was. Git can't necessarily figure out what +to do with your changes in that case.) + +If git hits such a conflict, rebasing will stop with a report on which files +are in a conflicted state. If you now look at these files with a text editor, +you will see that they contain sections that look like: + +```text +<<<<<<< HEAD +## This is how origin/main changed this file +======= +## And this is how your branch changed this file +>>>>>>> xxxxxxx (Updated doc/working-with-git-and-github.md) ``` -Get the latest changes for upstream: +(where xxxxxxx will be a seven-character hexadecimal "hash" identifying the +particular commit from your branch where these changes occurred). + +You need to manually edit this block, removing the `<<<<<<<` line, the +`=======` line, and the `>>>>>>>` line, and putting the contents in between to +be the way they "should" be considering both the changes that were made in the +main branch and in your branch. Sometimes this is easy: maybe `main` fixed a +typo in a comment and your branch inserted code just before the comment, and +so you just do both. Other times it is moot: `main` removed a function +altogether and you changed it in some way that is now irrelevant because the +function is gone. And other times it is quite tricky: you fixed a bug in some +function, but main changed its algorithm slightly and now it's not clear if +the bug is still there or if it is, whether your fix will still work. So you +may need to consider carefully and do some investigation and testing of your +new combined code; but most of the time it is pretty straightforward. It's +just that there aren't any hard-and-fast rules for how to resolve these sorts +of "overlapping changes" conflicts. If there were, git would just take care of +them automatically for you. + +Anyhow, when you've settled on the best way to resolve the conflict and have +edited all the conflicted files to have consistent code and have removed the +conflict markers, you can execute: ```sh -git fetch upstream +git rebase --continue ``` -Go to your main branch: +and the process will proceed further, either finishing up and reporting that +your branch is successfully rebased, or stopping again if further conflicts +are found. + +When the process finishes, you now have locally a version of your changes to +main that is based on the latest commit from main. It is a sort of rewriting +of history. If you had already pushed this branch to a remote, your local +version of the branch is now very different from the remote copy, and you will +want to force them to be the same again. You can do this with ```sh -git checkout main +git push -f ``` -Rewrite your main branch so that any commits of yours that aren't already in -upstream/main are replayed on top of upstream/main: +(where the `-f` stands for "force"). Finally, your branch is all back in sync +with main, and, for example, the process of your pull request being reviewed +can continue. -```sh -git rebase upstream/main -``` +Note that you will find many tutorials or references concerning Git that +recommend to use `git merge` to bring your branch up to date with additional +changes that have occurred in `main` since you branched. In our experience +working with Numberscope, that has led to more complicated PRs that are more +difficult to review. Hence, it is our recommendation that you use `git rebase` +instead of `git merge` for keeping your feature branch up-to-date. diff --git a/doc/working-with-pm.md b/doc/working-with-pm.md index f4216095..44bb1f93 100644 --- a/doc/working-with-pm.md +++ b/doc/working-with-pm.md @@ -8,8 +8,8 @@ an alternate package manager. {! ../package.json extract: {start: 'scripts', stop: '},', replace: [ - ['help:(\D*)".*".*echo (.*)"', '### npm run \1\n\2'], # one-liner like test - '"help:\D*:\d*".*echo (.*)"', # a numbered continuation line of help - ['"help:(\D*)":.*"', '### npm run \1\n'], # header of a block of help lines + ['help:(.*\D)".*".*echo (.*)"', '### npm run \1\n\2'], # 1-liner help + '"help:.*[-:]\d*".*echo \\?"?(.*?)\\?"?"', # a continuation line of help + ['"help:(.*\D)":.*"', '### npm run \1\n'], # header of block of help lines '.' # ignore the actual scripts that don't start with help ]} !} diff --git a/doc_theme/.main.html.generator b/doc_theme/.main.html.generator deleted file mode 100644 index c34ab0b6..00000000 --- a/doc_theme/.main.html.generator +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "base.html" %} - -{% block styles %} - {! ../index.html extract: {start: Bootstrap, stop: head} !} - {{ super() }} -{% endblock %} - -{% block topmatter %} -{! ../src/views/minor/NavBar.vue extract: - start: template - stop: template - replace: [ - ['^(.*)RouterLink(.*)to=(.*)$', '\1a\2href=\3'], - ['^(.*)RouterLink(.*)$', '\1a\2'], # For closing tag - ['^(.*):src="([^"]*)(".*)$', '\1src="/doc/src/assets/img/\2.png\3'], - ['copyright', 'Copyright © 2020-22 Regents of the University of Colorado'] - ] -!} -{% endblock %} diff --git a/doc_theme/css/.coordinate.css.generator b/doc_theme/css/.coordinate.css.generator deleted file mode 100644 index ce4bf2b2..00000000 --- a/doc_theme/css/.coordinate.css.generator +++ /dev/null @@ -1,6 +0,0 @@ -{! ../../src/views/minor/NavBar.vue extract: {start: 'style', stop: 'style'} !} -{! ../../src/App.vue extract: [ - {start: 'style', stop: 'main'}, - {start: '(.*nav.*)', stop: style} -] -!} diff --git a/e2e/Dockerfile b/e2e/Dockerfile new file mode 100644 index 00000000..f3d55619 --- /dev/null +++ b/e2e/Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/playwright:v1.47.0-noble + +# Copy frontscope (including tests) +COPY . /test + +WORKDIR /test + +# Clean out any sources/tests/build that happened to be there +RUN rm -rf src dist e2e/results/* e2e/tests + +# Install system dependencies +RUN apt update && apt install python3.12-venv -y + +# Install node dependencies +RUN npm install + +# Install browsers +RUN npx playwright install diff --git a/e2e/playci.config.ts b/e2e/playci.config.ts new file mode 100644 index 00000000..11cb7090 --- /dev/null +++ b/e2e/playci.config.ts @@ -0,0 +1,13 @@ +import {defineConfig} from '@playwright/test' + +import {baseConfiguration} from './playwright.config.ts' + +const dockerConfig = { + ...baseConfiguration, + snapshotDir: baseConfiguration.testDir + '/ci_snaps', + grepInvert: /@webGL/, + timeout: 30000, + reporter: [['list', {printSteps: true}]], +} + +export default defineConfig(dockerConfig) diff --git a/e2e/playdocker.config.ts b/e2e/playdocker.config.ts new file mode 100644 index 00000000..f9cefc31 --- /dev/null +++ b/e2e/playdocker.config.ts @@ -0,0 +1,33 @@ +import {defineConfig} from '@playwright/test' +import * as process from 'process' + +import {baseConfiguration} from './playwright.config.ts' + +const chromiumProject = baseConfiguration.projects.find( + proj => proj.name === 'chromium' +) +const firefoxProject = baseConfiguration.projects.find( + proj => proj.name === 'firefox' +) + +if ( + !chromiumProject + || !firefoxProject + || baseConfiguration.projects.length !== 2 +) { + console.error( + 'Base playwright configuration has changed, please update' + + 'e2e/playdocker.config.ts' + ) + process.exit(1) +} + +const dockerConfig = { + ...baseConfiguration, + outputDir: './results/docker/output', + timeout: 45000, + projects: [chromiumProject, {...firefoxProject, grepInvert: /@webGL/}], + reporter: [['blob', {outputFile: './results/docker/report.zip'}]], +} + +export default defineConfig(dockerConfig) diff --git a/e2e/playmerge.config.ts b/e2e/playmerge.config.ts new file mode 100644 index 00000000..784ebfa3 --- /dev/null +++ b/e2e/playmerge.config.ts @@ -0,0 +1,16 @@ +import {defineConfig} from '@playwright/test' + +import {baseConfiguration} from './playwright.config.ts' + +export default defineConfig({ + testDir: baseConfiguration.testDir, + reporter: [ + [ + 'html', + { + open: 'never', + outputFolder: './results/combined/playwright-report', + }, + ], + ], +}) diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts new file mode 100644 index 00000000..6e22ad77 --- /dev/null +++ b/e2e/playwright.config.ts @@ -0,0 +1,71 @@ +import {defineConfig, devices} from '@playwright/test' +import * as process from 'process' + +/** + * See https://playwright.dev/docs/test-configuration. + */ + +// Export the configuration object so we can derive from it in other tests +export const baseConfiguration = { + testDir: './tests', + outputDir: './results/manual/output', + /* Run tests in files in parallel */ + fullyParallel: false, + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: [ + ['html', {open: 'never', outputFolder: './results/manual/report'}], + ], + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://localhost:5050', + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: {...devices['Desktop Chrome']}, + }, + + { + name: 'firefox', + use: {...devices['Desktop Firefox']}, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'npm run preview:cmd', + url: 'http://localhost:5050', + reuseExistingServer: !process.env.CI, + }, +} + +/* Export the configuration for Playwright to use: */ +export default defineConfig(baseConfiguration) diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/BafflingBeattyBars-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/BafflingBeattyBars-chromium-linux.png new file mode 100644 index 00000000..62a7b3d5 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/BafflingBeattyBars-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/BafflingBeattyBars-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/BafflingBeattyBars-firefox-linux.png new file mode 100644 index 00000000..ceb7a441 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/BafflingBeattyBars-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ChaosGame-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ChaosGame-chromium-linux.png new file mode 100644 index 00000000..0a4df350 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ChaosGame-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ChaosGame-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ChaosGame-firefox-linux.png new file mode 100644 index 00000000..276685e3 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ChaosGame-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Danceno-163-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Danceno-163-chromium-linux.png new file mode 100644 index 00000000..120b430a Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Danceno-163-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Danceno-163-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Danceno-163-firefox-linux.png new file mode 100644 index 00000000..d7777349 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Danceno-163-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/DivisorSquare-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/DivisorSquare-chromium-linux.png new file mode 100644 index 00000000..0d921f49 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/DivisorSquare-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/DivisorSquare-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/DivisorSquare-firefox-linux.png new file mode 100644 index 00000000..f3e92f52 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/DivisorSquare-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Latticework-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Latticework-chromium-linux.png new file mode 100644 index 00000000..7e932897 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Latticework-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Latticework-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Latticework-firefox-linux.png new file mode 100644 index 00000000..ef942f9c Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Latticework-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Picasso-sPeriods-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Picasso-sPeriods-chromium-linux.png new file mode 100644 index 00000000..99950504 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Picasso-sPeriods-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Picasso-sPeriods-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Picasso-sPeriods-firefox-linux.png new file mode 100644 index 00000000..dbb6e25b Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Picasso-sPeriods-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/PrimeResidues-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/PrimeResidues-chromium-linux.png new file mode 100644 index 00000000..b8e49139 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/PrimeResidues-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/PrimeResidues-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/PrimeResidues-firefox-linux.png new file mode 100644 index 00000000..1b56cb88 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/PrimeResidues-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ResidueRise-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ResidueRise-chromium-linux.png new file mode 100644 index 00000000..4939626c Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ResidueRise-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ResidueRise-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ResidueRise-firefox-linux.png new file mode 100644 index 00000000..0073a969 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ResidueRise-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/TauManyPrimes-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/TauManyPrimes-chromium-linux.png new file mode 100644 index 00000000..4bfbbcc9 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/TauManyPrimes-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/TauManyPrimes-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/TauManyPrimes-firefox-linux.png new file mode 100644 index 00000000..880db390 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/TauManyPrimes-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ThueTrellis-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ThueTrellis-chromium-linux.png new file mode 100644 index 00000000..a4390103 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ThueTrellis-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ThueTrellis-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ThueTrellis-firefox-linux.png new file mode 100644 index 00000000..76b10eb9 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/ThueTrellis-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Twelve-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Twelve-chromium-linux.png new file mode 100644 index 00000000..9d41218e Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Twelve-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Twelve-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Twelve-firefox-linux.png new file mode 100644 index 00000000..a68cee18 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Twelve-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-chromium-linux.png new file mode 100644 index 00000000..ab6462a5 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-firefox-linux.png new file mode 100644 index 00000000..c8595dcd Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WaitForIt-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WaitForIt-chromium-linux.png new file mode 100644 index 00000000..e349a151 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WaitForIt-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WaitForIt-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WaitForIt-firefox-linux.png new file mode 100644 index 00000000..acc62a36 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WaitForIt-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WovenResidues-chromium-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WovenResidues-chromium-linux.png new file mode 100644 index 00000000..10746f82 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WovenResidues-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WovenResidues-firefox-linux.png b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WovenResidues-firefox-linux.png new file mode 100644 index 00000000..eb9fa271 Binary files /dev/null and b/e2e/tests/ci_snaps/featured.spec.ts-snapshots/WovenResidues-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-chromium-linux.png b/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-chromium-linux.png new file mode 100644 index 00000000..6fd0f6cf Binary files /dev/null and b/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-firefox-linux.png b/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-firefox-linux.png new file mode 100644 index 00000000..6b120fa0 Binary files /dev/null and b/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-chromium-linux.png b/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-chromium-linux.png new file mode 100644 index 00000000..87f101e5 Binary files /dev/null and b/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-firefox-linux.png b/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-firefox-linux.png new file mode 100644 index 00000000..123f21e6 Binary files /dev/null and b/e2e/tests/ci_snaps/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA007235-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA007235-chromium-linux.png new file mode 100644 index 00000000..e93f6bd7 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA007235-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA007235-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA007235-firefox-linux.png new file mode 100644 index 00000000..49ca5347 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA007235-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA114592-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA114592-chromium-linux.png new file mode 100644 index 00000000..15c011dd Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA114592-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA114592-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA114592-firefox-linux.png new file mode 100644 index 00000000..2cee9a99 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA114592-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA228060-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA228060-chromium-linux.png new file mode 100644 index 00000000..c18747c0 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA228060-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA228060-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA228060-firefox-linux.png new file mode 100644 index 00000000..41ff7ada Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ChaosA228060-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA001489-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA001489-chromium-linux.png new file mode 100644 index 00000000..f2ed5bed Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA001489-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA001489-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA001489-firefox-linux.png new file mode 100644 index 00000000..9fe583fa Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA001489-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA086677-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA086677-chromium-linux.png new file mode 100644 index 00000000..7e1c4271 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA086677-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA086677-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA086677-firefox-linux.png new file mode 100644 index 00000000..4f208230 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA086677-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA202319-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA202319-chromium-linux.png new file mode 100644 index 00000000..c2da32f3 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA202319-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA202319-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA202319-firefox-linux.png new file mode 100644 index 00000000..c837476b Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/DifferencesA202319-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA001489-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA001489-chromium-linux.png new file mode 100644 index 00000000..d3c1f928 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA001489-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA001489-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA001489-firefox-linux.png new file mode 100644 index 00000000..927ddb93 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA001489-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA134028-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA134028-chromium-linux.png new file mode 100644 index 00000000..5bc17d87 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA134028-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA134028-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA134028-firefox-linux.png new file mode 100644 index 00000000..329aab7c Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA134028-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA202319-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA202319-chromium-linux.png new file mode 100644 index 00000000..4089a413 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA202319-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA202319-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA202319-firefox-linux.png new file mode 100644 index 00000000..8c03364a Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA202319-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA241298-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA241298-chromium-linux.png new file mode 100644 index 00000000..3e0b3c25 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA241298-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA241298-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA241298-firefox-linux.png new file mode 100644 index 00000000..f0dc5d7e Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/FactorFenceA241298-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA002819-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA002819-chromium-linux.png new file mode 100644 index 00000000..0177c8ff Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA002819-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA002819-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA002819-firefox-linux.png new file mode 100644 index 00000000..42a6fd03 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA002819-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA134028-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA134028-chromium-linux.png new file mode 100644 index 00000000..77a00d9b Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA134028-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA134028-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA134028-firefox-linux.png new file mode 100644 index 00000000..c92b8ae7 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ModFillA134028-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA007235-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA007235-chromium-linux.png new file mode 100644 index 00000000..eb417fe4 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA007235-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA007235-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA007235-firefox-linux.png new file mode 100644 index 00000000..763a57d4 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA007235-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA114592-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA114592-chromium-linux.png new file mode 100644 index 00000000..cc613eb4 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA114592-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA114592-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA114592-firefox-linux.png new file mode 100644 index 00000000..21e1596c Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA114592-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA228060-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA228060-chromium-linux.png new file mode 100644 index 00000000..71001c69 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA228060-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA228060-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA228060-firefox-linux.png new file mode 100644 index 00000000..38839114 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA228060-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA241298-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA241298-chromium-linux.png new file mode 100644 index 00000000..a4093875 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA241298-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA241298-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA241298-firefox-linux.png new file mode 100644 index 00000000..953bd3f2 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/NumberGlyphA241298-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA001220-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA001220-chromium-linux.png new file mode 100644 index 00000000..bb77f858 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA001220-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA001220-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA001220-firefox-linux.png new file mode 100644 index 00000000..d642ed82 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA001220-firefox-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA086677-chromium-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA086677-chromium-linux.png new file mode 100644 index 00000000..7430cd18 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA086677-chromium-linux.png differ diff --git a/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA086677-firefox-linux.png b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA086677-firefox-linux.png new file mode 100644 index 00000000..0cfe4224 Binary files /dev/null and b/e2e/tests/ci_snaps/transversal.spec.ts-snapshots/ShowFactorsA086677-firefox-linux.png differ diff --git a/e2e/tests/doc.spec.ts b/e2e/tests/doc.spec.ts new file mode 100644 index 00000000..86d03912 --- /dev/null +++ b/e2e/tests/doc.spec.ts @@ -0,0 +1,10 @@ +import {test, expect} from '@playwright/test' + +test.describe('Documentation site', () => { + test('Includes husky actions', async ({page}) => { + await page.goto('/doc/doc/husky-pre-commit/') + await expect( + await page.locator('.hljs-built_in').nth(0).innerText() + ).toEqual('test') + }) +}) diff --git a/e2e/tests/featured.spec.ts b/e2e/tests/featured.spec.ts new file mode 100644 index 00000000..934291b9 --- /dev/null +++ b/e2e/tests/featured.spec.ts @@ -0,0 +1,33 @@ +import {test, expect} from '@playwright/test' + +import {getFeatured} from '../../src/shared/defineFeatured' +import {parseSpecimenQuery} from '../../src/shared/specimenEncoding' + +const featured = getFeatured() +test.describe('Featured gallery images', () => { + for (const feature of featured) { + const featProps = parseSpecimenQuery(feature.query) + const details = {} + if ( + featProps.visualizerKind === 'Histogram' + || featProps.visualizerKind === 'Turtle' + ) { + details.tag = '@webGL' + } + test(featProps.name, details, async ({page, browserName}) => { + const short = featProps.name.replaceAll(' ', '') + const testURL = `/?frames=64&randomSeed=${short}&${feature.query}` + await page.goto(testURL) + await expect( + page.locator('#specimen-bar-desktop').getByText('play_arrow') + ).toHaveId('pause-button', {timeout: 30000}) + const matchParams = + browserName === 'firefox' && details.tag === '@webGL' + ? {maxDiffPixelRatio: 0.02} + : {} + expect( + await page.locator('#canvas-container').screenshot() + ).toMatchSnapshot(`${short}.png`, matchParams) + }) + } +}) diff --git a/e2e/tests/featured.spec.ts-snapshots/BafflingBeattyBars-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/BafflingBeattyBars-chromium-linux.png new file mode 100644 index 00000000..62a7b3d5 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/BafflingBeattyBars-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/BafflingBeattyBars-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/BafflingBeattyBars-firefox-linux.png new file mode 100644 index 00000000..ceb7a441 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/BafflingBeattyBars-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/BeattyDNA-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/BeattyDNA-chromium-linux.png new file mode 100644 index 00000000..24d9f672 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/BeattyDNA-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/BeattyDNA-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/BeattyDNA-firefox-linux.png new file mode 100644 index 00000000..8857e1ab Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/BeattyDNA-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/ChaosGame-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/ChaosGame-chromium-linux.png new file mode 100644 index 00000000..0a4df350 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/ChaosGame-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/ChaosGame-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/ChaosGame-firefox-linux.png new file mode 100644 index 00000000..276685e3 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/ChaosGame-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Coprimality-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/Coprimality-chromium-linux.png new file mode 100644 index 00000000..b88fef49 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Coprimality-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Coprimality-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/Coprimality-firefox-linux.png new file mode 100644 index 00000000..0e09be69 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Coprimality-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Danceno-163-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/Danceno-163-chromium-linux.png new file mode 100644 index 00000000..120b430a Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Danceno-163-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Danceno-163-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/Danceno-163-firefox-linux.png new file mode 100644 index 00000000..d7777349 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Danceno-163-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/DivisorSquare-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/DivisorSquare-chromium-linux.png new file mode 100644 index 00000000..0d921f49 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/DivisorSquare-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/DivisorSquare-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/DivisorSquare-firefox-linux.png new file mode 100644 index 00000000..f3e92f52 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/DivisorSquare-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/GaussianSplitPrimeSoup-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/GaussianSplitPrimeSoup-chromium-linux.png new file mode 100644 index 00000000..bfa260d6 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/GaussianSplitPrimeSoup-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/GaussianSplitPrimeSoup-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/GaussianSplitPrimeSoup-firefox-linux.png new file mode 100644 index 00000000..12098e1e Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/GaussianSplitPrimeSoup-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/IntegerStellar-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/IntegerStellar-chromium-linux.png new file mode 100644 index 00000000..2ae98c97 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/IntegerStellar-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/IntegerStellar-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/IntegerStellar-firefox-linux.png new file mode 100644 index 00000000..00551a9f Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/IntegerStellar-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Latticework-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/Latticework-chromium-linux.png new file mode 100644 index 00000000..7e932897 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Latticework-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Latticework-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/Latticework-firefox-linux.png new file mode 100644 index 00000000..ef942f9c Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Latticework-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/ModularMultiplicationTable-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/ModularMultiplicationTable-chromium-linux.png new file mode 100644 index 00000000..fdcdd422 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/ModularMultiplicationTable-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/ModularMultiplicationTable-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/ModularMultiplicationTable-firefox-linux.png new file mode 100644 index 00000000..fc3d829e Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/ModularMultiplicationTable-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Picasso-sPeriods-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/Picasso-sPeriods-chromium-linux.png new file mode 100644 index 00000000..99950504 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Picasso-sPeriods-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Picasso-sPeriods-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/Picasso-sPeriods-firefox-linux.png new file mode 100644 index 00000000..dbb6e25b Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Picasso-sPeriods-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Polyfactors-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/Polyfactors-chromium-linux.png new file mode 100644 index 00000000..ce34b83f Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Polyfactors-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Polyfactors-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/Polyfactors-firefox-linux.png new file mode 100644 index 00000000..62713649 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Polyfactors-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/PrimeResidues-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/PrimeResidues-chromium-linux.png new file mode 100644 index 00000000..b8e49139 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/PrimeResidues-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/PrimeResidues-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/PrimeResidues-firefox-linux.png new file mode 100644 index 00000000..1b56cb88 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/PrimeResidues-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/ResidueRise-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/ResidueRise-chromium-linux.png new file mode 100644 index 00000000..4939626c Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/ResidueRise-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/ResidueRise-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/ResidueRise-firefox-linux.png new file mode 100644 index 00000000..0073a969 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/ResidueRise-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/SquareSwirl-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/SquareSwirl-chromium-linux.png new file mode 100644 index 00000000..611eac02 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/SquareSwirl-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/SquareSwirl-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/SquareSwirl-firefox-linux.png new file mode 100644 index 00000000..38fb03f6 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/SquareSwirl-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/TauManyPrimes-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/TauManyPrimes-chromium-linux.png new file mode 100644 index 00000000..efd72382 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/TauManyPrimes-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/TauManyPrimes-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/TauManyPrimes-firefox-linux.png new file mode 100644 index 00000000..7a2a95f0 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/TauManyPrimes-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/ThueTrellis-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/ThueTrellis-chromium-linux.png new file mode 100644 index 00000000..0733c8eb Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/ThueTrellis-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/ThueTrellis-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/ThueTrellis-firefox-linux.png new file mode 100644 index 00000000..76b10eb9 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/ThueTrellis-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Twelve-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/Twelve-chromium-linux.png new file mode 100644 index 00000000..9d41218e Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Twelve-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Twelve-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/Twelve-firefox-linux.png new file mode 100644 index 00000000..a68cee18 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Twelve-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/VFibSnowflake-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/VFibSnowflake-chromium-linux.png new file mode 100644 index 00000000..36d87e03 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/VFibSnowflake-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/VFibSnowflake-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/VFibSnowflake-firefox-linux.png new file mode 100644 index 00000000..81a8dcf0 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/VFibSnowflake-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-chromium-linux.png new file mode 100644 index 00000000..ab6462a5 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-firefox-linux.png new file mode 100644 index 00000000..c8595dcd Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/Virahanka-sPrimeConstruct-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/WaitForIt-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/WaitForIt-chromium-linux.png new file mode 100644 index 00000000..e349a151 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/WaitForIt-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/WaitForIt-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/WaitForIt-firefox-linux.png new file mode 100644 index 00000000..acc62a36 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/WaitForIt-firefox-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/WovenResidues-chromium-linux.png b/e2e/tests/featured.spec.ts-snapshots/WovenResidues-chromium-linux.png new file mode 100644 index 00000000..10746f82 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/WovenResidues-chromium-linux.png differ diff --git a/e2e/tests/featured.spec.ts-snapshots/WovenResidues-firefox-linux.png b/e2e/tests/featured.spec.ts-snapshots/WovenResidues-firefox-linux.png new file mode 100644 index 00000000..eb9fa271 Binary files /dev/null and b/e2e/tests/featured.spec.ts-snapshots/WovenResidues-firefox-linux.png differ diff --git a/e2e/tests/gallery.spec.ts b/e2e/tests/gallery.spec.ts new file mode 100644 index 00000000..d0524ddb --- /dev/null +++ b/e2e/tests/gallery.spec.ts @@ -0,0 +1,64 @@ +import {test, expect} from '@playwright/test' + +test.beforeEach(async ({page}) => { + await page.goto('/gallery', {waitUntil: 'domcontentloaded'}) + await page.evaluate(() => localStorage.clear()) +}) + +test.describe('Gallery', () => { + test('Has a title', async ({page}) => { + await expect(page).toHaveTitle(/Numberscope/) + }) + test('minimizing', async ({page}) => { + await expect(page.locator('#featured-arrow')).toHaveClass(/arrow-up/) + await expect(page.locator('#featured-arrow')).not.toHaveClass( + /arrow-down/ + ) + + await page.locator('#featured-arrow').first().click() + await expect(page.locator('#featured-arrow')).not.toHaveClass( + /arrow-up/ + ) + await expect(page.locator('#featured-arrow')).toHaveClass( + /arrow-down/ + ) + + await page.locator('#featured-arrow').first().click() + await expect(page.locator('#featured-arrow')).toHaveClass(/arrow-up/) + await expect(page.locator('#featured-arrow')).not.toHaveClass( + /arrow-down/ + ) + }) + test('clicking on a featured item', async ({page}) => { + await page.locator('.card-body >> nth=2').click() + await expect(page.url()).not.toContain('gallery') + await expect( + await page.locator('#sequenceTab .item-name') + ).toContainText(/163/) + await expect( + await page.locator('#visualiserTab .item-name').innerText() + ).toMatch('Mod Fill') + }) + test('saving a specimen and then deleting it', async ({page}) => { + // test originally written when the following was the default: + await page.goto('/?name=Specimen&viz=ModFill&seq=Random', { + waitUntil: 'domcontentloaded', + }) + await page.locator('#specimen-bar-desktop #save-button').click() + await page.goto('/gallery', {waitUntil: 'domcontentloaded'}) + + const savedCard = await page + .locator('#saved-gallery .card-body') + .first() + + await expect( + await savedCard.locator('.card-title').innerText() + ).toMatch('Specimen') + await expect( + await savedCard.locator('.card-text').innerText() + ).toMatch('Random integers 0 to 9') + + await savedCard.locator('.delete-button').click() + await expect(page.locator('#saved-specimens >> *')).toHaveCount(0) + }) +}) diff --git a/e2e/tests/idiot.spec.ts b/e2e/tests/idiot.spec.ts new file mode 100644 index 00000000..c155886e --- /dev/null +++ b/e2e/tests/idiot.spec.ts @@ -0,0 +1,38 @@ +import {test, expect} from '@playwright/test' + +import {specimenQuery} from '../../src/shared/specimenEncoding' + +// These are tests in the spirit of +// https://github.com/numberscope/frontscope/issues/113, i.e. +// "idiot-proofing" Numberscope + +// Each item is the argument list to specimenQuery +const testlist = { + 'Way too big a number': [ + 'A', + 'ModFill', + 'Formula', + 'modDimension=1111111111111111111111111111111111111111111', + 'formula=12', + ], + 'Start deep in a sequence': [ + 'B', + 'FactorFence', + 'Formula', + '', + 'first=1020000', + ], +} +test.describe('Stress-test Numberscope usage', () => { + for (const k in testlist) { + test(k, async ({page}) => { + const query = specimenQuery(...testlist[k]) + const testURL = '/?frames=5&' + query + await page.goto(testURL) + await expect( + page.locator('#specimen-bar-desktop').getByText('play_arrow') + ).toHaveId('pause-button', {timeout: 15000}) + await expect(page).toHaveScreenshot() // capture error alert, if one + }) + } +}) diff --git a/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-chromium-linux.png b/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-chromium-linux.png new file mode 100644 index 00000000..5868d5ab Binary files /dev/null and b/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-chromium-linux.png differ diff --git a/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-firefox-linux.png b/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-firefox-linux.png new file mode 100644 index 00000000..1c442dac Binary files /dev/null and b/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Start-deep-in-a-sequence-1-firefox-linux.png differ diff --git a/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-chromium-linux.png b/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-chromium-linux.png new file mode 100644 index 00000000..76dfff2c Binary files /dev/null and b/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-chromium-linux.png differ diff --git a/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-firefox-linux.png b/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-firefox-linux.png new file mode 100644 index 00000000..dcd4248b Binary files /dev/null and b/e2e/tests/idiot.spec.ts-snapshots/Stress-test-Numberscope-usage-Way-too-big-a-number-1-firefox-linux.png differ diff --git a/e2e/tests/scope.spec.ts b/e2e/tests/scope.spec.ts new file mode 100644 index 00000000..a2fda001 --- /dev/null +++ b/e2e/tests/scope.spec.ts @@ -0,0 +1,184 @@ +import {test, expect} from '@playwright/test' + +import {parseSpecimenQuery} from '../../src/shared/specimenEncoding' + +test.describe('Scope: on some featured visualization', () => { + test.beforeEach(async ({page}) => { + await page.goto('/', {waitUntil: 'domcontentloaded'}) + await page.evaluate(() => localStorage.clear()) + }) + + test('Has a title', async ({page}) => { + await expect(page).toHaveTitle(/Numberscope/) + }) + + test('Tabs are draggable', async ({page}) => { + await page + .locator('#visualiserTab .buttons') + .dragTo(page.locator('#canvas-container'), { + force: true, + sourcePosition: { + x: 10, + y: 10, + }, + targetPosition: { + x: 100, + y: 100, + }, + }) + await expect(page.locator('#bottom-right-dropzone')).toHaveClass( + /empty/ + ) + }) + + test('Sidebars disappear when tabs are dragged', async ({page}) => { + await page + .locator('#visualiserTab .buttons') + .dragTo(page.locator('#canvas-container'), { + force: true, + sourcePosition: { + x: 10, + y: 10, + }, + }) + + await page + .locator('#sequenceTab .buttons') + .dragTo(page.locator('#canvas-container'), { + force: true, + sourcePosition: { + x: 10, + y: 10, + }, + + targetPosition: { + x: 500, + y: 100, + }, + }) + + await expect(page.locator('#right-dropzone-container')).toHaveClass( + /empty/ + ) + await expect( + page.locator('#right-dropzone-container .dropzone-resize') + ).toHaveCSS('display', 'none') + + await expect(page.locator('#left-dropzone-container')).toHaveClass( + /empty/ + ) + await expect( + page.locator('#left-dropzone-container .dropzone-resize') + ).toHaveCSS('display', 'none') + }) + + test('Changing a sequence', async ({page}) => { + const lookFor = 'Random' + const andThenFind = 'Random integers 0 to 9' + await page.locator('#sequenceTab .visualizer-info').click() + await page.getByText(lookFor, {exact: true}).click() + await expect( + await page.locator('#sequenceTab .item-name').innerText() + ).toMatch(andThenFind) + }) + + test('minimizing a tab', async ({page}) => { + await expect(page.locator('#visualiserTab')).not.toHaveClass( + /minimized/ + ) + await page.locator('#visualiserTab .minimize').click() + await expect(page.locator('#visualiserTab')).toHaveClass(/minimized/) + await expect( + await page.locator('#visualiserTab').evaluate(element => { + return element.clientHeight + }) + ).toBeLessThan(125) + + await page.locator('#visualiserTab .minimize').click() + await expect(page.locator('#visualiserTab')).not.toHaveClass( + /minimized/ + ) + await expect( + await page.locator('#visualiserTab').evaluate(element => { + return element.clientHeight + }) + ).toBeGreaterThan(375) + }) + test('changing the specimen name', async ({page}) => { + const oldURL = page.url() + + await page.locator('#specimen-bar-desktop input').fill('test') + const currentSpecimenIM = await page.evaluate(() => { + return localStorage.getItem('currentSpecimen') + }) + if (currentSpecimenIM === null) { + throw new Error('currentSpecimen is null') + } + const currentSpecimen = JSON.parse(currentSpecimenIM) + const currentProperties = parseSpecimenQuery(currentSpecimen.query) + + await expect(currentProperties.name).toEqual('test') + await expect(page.url()).not.toEqual(oldURL) + }) + test('copying to clipboard', async ({page, context, browserName}) => { + // grant clipboard permissions for chromium, other browsers don't + // allow this due to privacy concerns + if (browserName === 'chromium') { + await context.grantPermissions([ + 'clipboard-read', + 'clipboard-write', + ]) + } + + await page.locator('#specimen-bar-desktop #share-button').click() + + let clipboardContent + if (browserName === 'chromium') { + const handle = await page.evaluateHandle(() => + navigator.clipboard.readText() + ) + clipboardContent = await handle.jsonValue() + } else { + // we can't read the clipboard content in other browsers + clipboardContent = page.url() + } + + const url = page.url() + await expect(clipboardContent).toMatch(url) + }) +}) + +test.describe('Scope: on Random Modfill', () => { + test.beforeEach(async ({page}) => { + await page.goto('/?name=Specimen&viz=ModFill&seq=Random', { + waitUntil: 'domcontentloaded', + }) + await page.evaluate(() => localStorage.clear()) + }) + + test('Changing a parameter, then using back button', async ({page}) => { + const oldURL = page.url() + + await page.locator('#modDimension').fill('100') + await expect(page.locator('#modDimension')).toHaveValue('100') + + await expect(page.url()).not.toEqual(oldURL) + + await page.goBack({waitUntil: 'domcontentloaded'}) + await expect(page.url()).toEqual(oldURL) + await expect(page.locator('#modDimension')).toHaveValue('150') + }) + + test('refreshing the specimen', async ({page}) => { + const oldCanvas = await page.locator('#canvas-container canvas') + + await oldCanvas.evaluate(canvas => { + canvas.classList.add('old-canvas') + }) + await page.locator('#specimen-bar-desktop #refresh-button').click() + + const newCanvas = await page.locator('#canvas-container canvas') + await expect(newCanvas).not.toBe(oldCanvas) + await expect(newCanvas).not.toHaveClass('old-canvas') + }) +}) diff --git a/e2e/tests/stress.spec.ts b/e2e/tests/stress.spec.ts new file mode 100644 index 00000000..44b79e1f --- /dev/null +++ b/e2e/tests/stress.spec.ts @@ -0,0 +1,34 @@ +import {test, expect} from '@playwright/test' + +import {specimenQuery} from '../../src/shared/specimenEncoding' + +// These are non-snapshot tests that we know to be somewhat or very +// challenging to Numberscope. Once sequences that are here pass, they +// might be candidates for adding to the transversal test. + +// Each item is the argument list to specimenQuery +const testlist = { + 'Sequence with lots of data': ['T1', 'Histogram', 'OEIS A000521'], + 'Sequence with extremely large offset': [ + 'T2', + 'Differences', + 'OEIS A241292', + ], +} +test.describe('Stress-test Numberscope visualization', () => { + for (const k in testlist) { + // Right now these all fail. This skipping will have to be selective + // as code improves and some switch to passing. + test.skip(k, async ({page}) => { + const query = specimenQuery(...testlist[k]) + const testURL = '/?frames=5&' + query + await page.goto(testURL) + await expect( + page.locator('#specimen-bar-desktop').getByText('play_arrow') + ).toHaveId('pause-button', {timeout: 15000}) + await expect( + await page.locator('#sequenceTab .description').innerText() + ).not.toMatch(/^Unknown OEIS sequence/) + }) + } +}) diff --git a/e2e/tests/transversal.spec.ts b/e2e/tests/transversal.spec.ts new file mode 100644 index 00000000..bb3e1979 --- /dev/null +++ b/e2e/tests/transversal.spec.ts @@ -0,0 +1,94 @@ +import fs from 'fs' +import {test, expect} from '@playwright/test' + +import {math} from '../../src/shared/math' +import {specimenQuery} from '../../src/shared/specimenEncoding' + +// The idea here is to take a collection of challenging sequences, and +// make sure that we try each sequence with at least two visualizers, and +// run each visualizer with at least two sequences. We do this by choosing +// two visualizers at random for each sequence, and then if any visualizer +// has not been chosen twice, we choose (an)other sequence(s) for it. We +// seed the random number generator so that the assignments will be +// reproducible. + +const seed = 'KILROY' // only change this in standalone PR +const challenges = [ + 'A114592', // Only -1,0,1, for 359 terms, then some bigger values + 'A007235', // short sequence but grows fast + 'A001220', // very few terms + // 'A000521', // long seq with -1 offset, grows fast; temp too much data + 'A228060', // offset -85, many small terms + 'A086677', // offset 2 + 'A241298', // very large positive offset + // 'A241292', // too large a positive offset (for now) + 'A134028', // mix of negative and positive terms + 'A002819', // nonpositive sequence + 'A001489', // all non-positive integers + 'A202319', // semiprimes +] // feel free to add more + +const vizFiles = fs + .readdirSync('./src/visualizers', {withFileTypes: true}) + .filter(item => !item.isDirectory()) + .map(item => item.name) +const vizKeys: string[] = [] +for (const vf of vizFiles) { + if (vf.includes('isualizer')) continue // infrastructure + if (vf.endsWith('.d.ts')) continue // infrastructure + if (vf.includes('ShiftCompare')) continue // freezes way too long + if (vf.includes('Turtle')) continue // Does nothing on most sequences + if (!vf.endsWith('.ts')) continue + vizKeys.push(vf.slice(0, -3)) +} +const vizSeqs = Object.fromEntries(vizKeys.map(k => [k, [] as string[]])) + +math.config({randomSeed: seed}) + +for (const seq of challenges) { + let count = 2 + while (count--) { + let viz = math.pickRandom(vizKeys) + while (vizSeqs[viz].includes(seq)) viz = math.pickRandom(vizKeys) + vizSeqs[viz].push(seq) + } +} +// Now make sure every visualizer got at least two sequences +for (const viz of vizKeys) { + let left = 2 - vizSeqs[viz].length + while (left-- > 0) { + // Find a sequence not used with this visualizers + let seq = math.pickRandom(challenges) + while (vizSeqs[viz].includes(seq)) seq = math.pickRandom(challenges) + vizSeqs[viz].push(seq) + } +} + +// now run all the combos +test.describe('Visualizer-sequence challenges', () => { + for (const viz of vizKeys) { + const vizPar = viz === 'Chaos' ? 'circSize=5' : '' // ow tough to see + const details = {} + if (viz === 'Histogram') { + details.tag = '@webGL' + } + for (const seq of vizSeqs[viz]) { + const query = specimenQuery(viz + seq, viz, 'OEIS ' + seq, vizPar) + test(`${seq} ${viz}`, details, async ({page, browserName}) => { + await page.goto(`/?frames=32&${query}`) + await expect( + page + .locator('#specimen-bar-desktop') + .getByText('play_arrow') + ).toHaveId('pause-button', {timeout: 20000}) + const matchParams = + browserName === 'firefox' && details.tag === '@webGL' + ? {maxDiffPixelRatio: 0.02} + : {} + expect( + await page.locator('#canvas-container').screenshot() + ).toMatchSnapshot(`${viz + seq}.png`, matchParams) + }) + } + } +}) diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA000521-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA000521-chromium-linux.png new file mode 100644 index 00000000..bfe487fb Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA000521-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA000521-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA000521-firefox-linux.png new file mode 100644 index 00000000..d15323f4 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA000521-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA007235-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA007235-chromium-linux.png new file mode 100644 index 00000000..e93f6bd7 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA007235-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA007235-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA007235-firefox-linux.png new file mode 100644 index 00000000..49ca5347 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA007235-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA114592-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA114592-chromium-linux.png new file mode 100644 index 00000000..15c011dd Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA114592-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA114592-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA114592-firefox-linux.png new file mode 100644 index 00000000..2cee9a99 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA114592-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA202319-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA202319-chromium-linux.png new file mode 100644 index 00000000..572c6dc6 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA202319-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA202319-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA202319-firefox-linux.png new file mode 100644 index 00000000..cd983cbb Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA202319-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA228060-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA228060-chromium-linux.png new file mode 100644 index 00000000..c18747c0 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA228060-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ChaosA228060-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ChaosA228060-firefox-linux.png new file mode 100644 index 00000000..41ff7ada Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ChaosA228060-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA001489-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA001489-chromium-linux.png new file mode 100644 index 00000000..dd6a9ade Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA001489-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA001489-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA001489-firefox-linux.png new file mode 100644 index 00000000..bd4aa0bc Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA001489-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA002819-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA002819-chromium-linux.png new file mode 100644 index 00000000..cc2f1481 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA002819-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA002819-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA002819-firefox-linux.png new file mode 100644 index 00000000..6a7e1477 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA002819-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA086677-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA086677-chromium-linux.png new file mode 100644 index 00000000..d5437a6f Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA086677-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA086677-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA086677-firefox-linux.png new file mode 100644 index 00000000..cb9e715e Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA086677-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA202319-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA202319-chromium-linux.png new file mode 100644 index 00000000..366fb6cb Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA202319-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA202319-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA202319-firefox-linux.png new file mode 100644 index 00000000..5c66975d Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA202319-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA228060-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA228060-chromium-linux.png new file mode 100644 index 00000000..8998f579 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA228060-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/DifferencesA228060-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA228060-firefox-linux.png new file mode 100644 index 00000000..96a60f7d Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/DifferencesA228060-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA001489-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA001489-chromium-linux.png new file mode 100644 index 00000000..88680a93 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA001489-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA001489-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA001489-firefox-linux.png new file mode 100644 index 00000000..a9b198ee Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA001489-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA002819-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA002819-chromium-linux.png new file mode 100644 index 00000000..332a6176 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA002819-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA002819-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA002819-firefox-linux.png new file mode 100644 index 00000000..9ef298db Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA002819-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA086677-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA086677-chromium-linux.png new file mode 100644 index 00000000..42e50b14 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA086677-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA086677-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA086677-firefox-linux.png new file mode 100644 index 00000000..844c2e61 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA086677-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA134028-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA134028-chromium-linux.png new file mode 100644 index 00000000..0bf4378e Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA134028-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA134028-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA134028-firefox-linux.png new file mode 100644 index 00000000..9749d714 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA134028-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA202319-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA202319-chromium-linux.png new file mode 100644 index 00000000..16e3346f Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA202319-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA202319-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA202319-firefox-linux.png new file mode 100644 index 00000000..7cc5e088 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA202319-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA228060-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA228060-chromium-linux.png new file mode 100644 index 00000000..23dca35d Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA228060-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA228060-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA228060-firefox-linux.png new file mode 100644 index 00000000..4028f1fb Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA228060-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA241298-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA241298-chromium-linux.png new file mode 100644 index 00000000..44e50082 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA241298-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA241298-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA241298-firefox-linux.png new file mode 100644 index 00000000..e409692b Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/FactorFenceA241298-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA001220-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA001220-chromium-linux.png new file mode 100644 index 00000000..64c31df1 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA001220-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA001220-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA001220-firefox-linux.png new file mode 100644 index 00000000..2dde5abf Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA001220-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA002819-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA002819-chromium-linux.png new file mode 100644 index 00000000..b1213926 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA002819-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA002819-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA002819-firefox-linux.png new file mode 100644 index 00000000..37df4602 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA002819-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA134028-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA134028-chromium-linux.png new file mode 100644 index 00000000..184b436b Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA134028-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA134028-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA134028-firefox-linux.png new file mode 100644 index 00000000..de61e0b2 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA134028-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA202319-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA202319-chromium-linux.png new file mode 100644 index 00000000..c860fa84 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA202319-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA202319-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA202319-firefox-linux.png new file mode 100644 index 00000000..14bf155b Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA202319-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA241298-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA241298-chromium-linux.png new file mode 100644 index 00000000..28057072 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA241298-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/HistogramA241298-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/HistogramA241298-firefox-linux.png new file mode 100644 index 00000000..a4cb4064 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/HistogramA241298-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA001220-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA001220-chromium-linux.png new file mode 100644 index 00000000..c8cd086d Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA001220-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA001220-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA001220-firefox-linux.png new file mode 100644 index 00000000..6e5d5027 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA001220-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA002819-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA002819-chromium-linux.png new file mode 100644 index 00000000..0177c8ff Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA002819-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA002819-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA002819-firefox-linux.png new file mode 100644 index 00000000..42a6fd03 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA002819-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA134028-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA134028-chromium-linux.png new file mode 100644 index 00000000..77a00d9b Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA134028-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA134028-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA134028-firefox-linux.png new file mode 100644 index 00000000..c92b8ae7 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA134028-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA202319-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA202319-chromium-linux.png new file mode 100644 index 00000000..0700f245 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA202319-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA202319-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA202319-firefox-linux.png new file mode 100644 index 00000000..e1ab1673 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA202319-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA241298-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA241298-chromium-linux.png new file mode 100644 index 00000000..f53d86a4 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA241298-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ModFillA241298-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ModFillA241298-firefox-linux.png new file mode 100644 index 00000000..998c0609 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ModFillA241298-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA000521-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA000521-chromium-linux.png new file mode 100644 index 00000000..d32b0f69 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA000521-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA000521-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA000521-firefox-linux.png new file mode 100644 index 00000000..7ac64aaf Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA000521-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA007235-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA007235-chromium-linux.png new file mode 100644 index 00000000..eb417fe4 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA007235-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA007235-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA007235-firefox-linux.png new file mode 100644 index 00000000..763a57d4 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA007235-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA086677-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA086677-chromium-linux.png new file mode 100644 index 00000000..1e60d06d Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA086677-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA086677-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA086677-firefox-linux.png new file mode 100644 index 00000000..8fb14843 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA086677-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA114592-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA114592-chromium-linux.png new file mode 100644 index 00000000..ae2b8651 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA114592-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA114592-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA114592-firefox-linux.png new file mode 100644 index 00000000..364ba132 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA114592-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA134028-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA134028-chromium-linux.png new file mode 100644 index 00000000..00a61d7f Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA134028-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA134028-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA134028-firefox-linux.png new file mode 100644 index 00000000..e42f9886 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA134028-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA202319-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA202319-chromium-linux.png new file mode 100644 index 00000000..58475e1f Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA202319-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA202319-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA202319-firefox-linux.png new file mode 100644 index 00000000..a00be347 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA202319-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA228060-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA228060-chromium-linux.png new file mode 100644 index 00000000..71001c69 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA228060-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA228060-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA228060-firefox-linux.png new file mode 100644 index 00000000..38839114 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA228060-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA241298-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA241298-chromium-linux.png new file mode 100644 index 00000000..a4093875 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA241298-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA241298-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA241298-firefox-linux.png new file mode 100644 index 00000000..953bd3f2 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/NumberGlyphA241298-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA007235-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA007235-chromium-linux.png new file mode 100644 index 00000000..8493148f Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA007235-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA007235-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA007235-firefox-linux.png new file mode 100644 index 00000000..e4a9b3f6 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA007235-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA114592-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA114592-chromium-linux.png new file mode 100644 index 00000000..8493148f Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA114592-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA114592-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA114592-firefox-linux.png new file mode 100644 index 00000000..e4a9b3f6 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA114592-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA228060-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA228060-chromium-linux.png new file mode 100644 index 00000000..44e88c60 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA228060-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA228060-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA228060-firefox-linux.png new file mode 100644 index 00000000..ff5fd918 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA228060-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA241298-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA241298-chromium-linux.png new file mode 100644 index 00000000..8493148f Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA241298-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA241298-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA241298-firefox-linux.png new file mode 100644 index 00000000..e4a9b3f6 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/SelfSimilarityA241298-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA000521-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA000521-chromium-linux.png new file mode 100644 index 00000000..6f5e4ab2 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA000521-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA000521-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA000521-firefox-linux.png new file mode 100644 index 00000000..204d2903 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA000521-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA001220-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA001220-chromium-linux.png new file mode 100644 index 00000000..cff1a996 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA001220-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA001220-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA001220-firefox-linux.png new file mode 100644 index 00000000..047da2b4 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA001220-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA007235-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA007235-chromium-linux.png new file mode 100644 index 00000000..5278910f Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA007235-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA007235-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA007235-firefox-linux.png new file mode 100644 index 00000000..01b0ca5e Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA007235-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA086677-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA086677-chromium-linux.png new file mode 100644 index 00000000..906fb935 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA086677-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA086677-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA086677-firefox-linux.png new file mode 100644 index 00000000..e4214f9d Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA086677-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA114592-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA114592-chromium-linux.png new file mode 100644 index 00000000..2d8fc313 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA114592-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA114592-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA114592-firefox-linux.png new file mode 100644 index 00000000..5cf97b05 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA114592-firefox-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA228060-chromium-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA228060-chromium-linux.png new file mode 100644 index 00000000..28f7dec8 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA228060-chromium-linux.png differ diff --git a/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA228060-firefox-linux.png b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA228060-firefox-linux.png new file mode 100644 index 00000000..74e8ce04 Binary files /dev/null and b/e2e/tests/transversal.spec.ts-snapshots/ShowFactorsA228060-firefox-linux.png differ diff --git a/env.d.ts b/env.d.ts deleted file mode 100644 index 11f02fe2..00000000 --- a/env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/etc/Makefile b/etc/Makefile new file mode 100644 index 00000000..655583f4 --- /dev/null +++ b/etc/Makefile @@ -0,0 +1,138 @@ +.RECIPEPREFIX = | +.PHONY: FORCE playreport phony typecheck vueserve docserve preview prettiest + +SHELL = /bin/bash + +sources != shopt -s globstar && echo src/** index.html +docs != shopt -s globstar &&\ + echo doc/** etc/doc_theme/** README.md CONTRIBUTING.md etc/mkdocs.yml +tests != shopt -s globstar && echo e2e/tests/** +manifest := package.json package-lock.json requirements.txt tools/pyrun.mjs $(wildcard .husky/p*) +typescript_config := $(wildcard tsconfig*json) +e2e_local_config := e2e/playwright.config.ts +e2e_docker_config := $(e2e_local_config) e2e/playdocker.config.ts e2e/Dockerfile + +# Dummy files +installed := etc/npm_install_ran_at +typechecked := etc/typechecked_at +docked := e2e/docker_image_built + +E2E_IMAGE := frontscope-e2e-image +E2E_CONTAINER := frontscope-e2e-container + +# Default target: build the distribution in the dist/ directory +dist: $(sources) $(docs) $(install) $(typechecked) etc/vite.config.ts etc/Makefile +| npx vite --config etc/vite.config.ts build +| node tools/pyrun.mjs mkdocs build --strict --config-file etc/mkdocs.yml +| touch $@ + +# Make sometimes thinks it needs to make the following target because of some +# weirdness of how `sh -c 'COMMAND' NAME` works, or at least so I guess: +phony: +| @echo + +$(installed): $(manifest) +| npm install +| touch $@ + +$(typechecked): $(installed) $(typescript_config) $(sources) +| npx vue-tsc --noEmit -p tsconfig.vitest.json --composite false +| @touch $@ + +prettiest: $(installed) +| @node tools/prettiest.js $(flags) . + +typecheck: $(typechecked) +| @echo TypeScript types check. + +vueserve: $(typechecked) +| npx vite --config etc/vite.config.ts $(if $(mode),--mode=$(mode)) + +docserve: $(installed) +| node tools/pyrun.mjs mkdocs serve --strict -f $(or $(config),etc/mkdocs.yml) + +preview: dist +| npm run preview:cmd + +testunit: $(typechecked) +| npx vitest --config etc/vitest.config.ts run $(cl_args) + +# Makes a Dockerfile that can be reused for multiple test runs +$(docked): $(manifest) $(e2e_docker_config) +| docker build -t $(E2E_IMAGE) -f e2e/Dockerfile . +| touch $@ + +last_cl_args != touch -a e2e/.last_cl_args && cat e2e/.last_cl_args + +# If no arguments are given to playreport, assume just want the last +# playwright test that has been run +ifeq ($(MAKECMDGOALS),playreport) +ifeq ($(cl_args),) +override cl_args := $(last_cl_args) +endif +endif +cl_arg_dep = +ifeq ($(last_cl_args),$(cl_args)) +else +e2e/.last_cl_args: FORCE +| @echo Detected new testing command-line arguments: +| @echo " '"$(cl_args)\' vs \'$(last_cl_args)\' +| echo $(cl_args) > $@ + +cl_arg_dep = e2e/.last_cl_args +endif + +# OK, to certify that all end-to-end tests are in good shape, we have +# to run the non-WebGL non-firefox tests in docker, the firefox WebGL tests +# locally, merge the two reports, and then check if everything passed (whew!) +e2e/certificate: e2e/results/combined/playwright-report e2e/results/docker e2e/results/local +| @grep 'status.*passed' e2e/results/docker/output/.last-run.json +| @grep 'status.*passed' e2e/results/local/output/.last-run.json +| echo 'All tests passed' > $@ + +e2e/results/combined/playwright-report: e2e/results/docker e2e/results/local e2e/playmerge.config.ts +| mkdir -p e2e/results/combined +| cp e2e/results/docker/report.zip e2e/results/combined/report-docker.zip +| cp e2e/results/local/report.zip e2e/results/combined/report-local.zip +| npx playwright merge-reports --config=e2e/playmerge.config.ts e2e/results/combined +| touch $@ + +# Here we run webgl tests in firefox on the local machine +# Since there are not many of them, we could easily miss all of them +# with a command-line argument, so pass even if we don't find any +# matching tests. +e2e/results/local: dist $(e2e_local_config) $(tests) $(cl_arg_dep) +| PLAYWRIGHT_BLOB_OUTPUT_FILE=$@/report.zip npm run test:e2e:cmd -- --grep @webGL --project firefox --pass-with-no-tests --output $@/output --reporter blob $(cl_args) || echo [Firefox webGL tests failed] +| touch $@ + +# And here we run all the other tests under Docker +# Here it is likely an error if there are no tests +e2e/results/docker: $(docked) dist $(e2e_docker_config) $(tests) $(sources) $(cl_arg_dep) +# Start the container with a dummy command to keep it alive +| docker run --name $(E2E_CONTAINER) --rm -d -i -t $(E2E_IMAGE) /bin/sh +# Copy the current source, build, and tests to the container +| docker cp dist $(E2E_CONTAINER):/test +| docker cp src $(E2E_CONTAINER):/test +| docker cp e2e/tests $(E2E_CONTAINER):/test/e2e +# Run the tests! Since we have to use a different config, sadly no way +# to reuse the npm command, so we just roll our own: +| docker exec $(E2E_CONTAINER) npx playwright test -c e2e/playdocker.config.ts $(cl_args) || echo '[Docker tests failed]' +# Recreate the results directory +| rm -rf $@ +# Extract the results from the container +| mkdir -p e2e/results +| docker cp $(E2E_CONTAINER):/test/$@ $@ +ifeq (,$(findstring --update-snapshots,$(cl_args))) +else +| docker cp $(E2E_CONTAINER):/test/e2e/tests $@ +| cp -r $@/tests/*-snapshots e2e/tests +endif +# Clean up +| docker stop $(E2E_CONTAINER) +| touch $@ + +# finally, report on the full combined tests: + +playreport: e2e/results/combined/playwright-report +| playwright show-report $^ + diff --git a/doc_theme/.base.html.generator b/etc/doc_theme/.base.html.generator similarity index 100% rename from doc_theme/.base.html.generator rename to etc/doc_theme/.base.html.generator diff --git a/etc/doc_theme/.main.html.generator b/etc/doc_theme/.main.html.generator new file mode 100644 index 00000000..ad130e7c --- /dev/null +++ b/etc/doc_theme/.main.html.generator @@ -0,0 +1,26 @@ +{% extends "base.html" %} + +{% block styles %} + {! ../../index.html extract: {start: Bootstrap, stop: head} !} + {{ super() }} +{% endblock %} + +{% block topmatter %} +{! ../../src/views/minor/NavBar.vue extract: + start: '[<]template' + stop: help-popper + replace: [ + ['^(.*)doc/doc/about/(.*)$', '\1\2'], # Logo goes back to scope + ['^(.*):src="([^"]*)(".*)$', '\1src="/doc/src/assets/img/logo.svg\3'], + ['^(.*)RouterLink$', '\1a'], + ['^(\s*)to=(.*)', '\1href=\2'], + ['[<]/RouterLink.', ''] + ] +!} + Back to Numberscope + +
close
+ + + +{% endblock %} diff --git a/doc_theme/css/override.css b/etc/doc_theme/css/override.css similarity index 58% rename from doc_theme/css/override.css rename to etc/doc_theme/css/override.css index 37aa4f6b..ae7fc56b 100644 --- a/doc_theme/css/override.css +++ b/etc/doc_theme/css/override.css @@ -5,13 +5,11 @@ header { z-index: 250; } -nav { - font-family: Arial, Helvetica, sans-serif; -} - .wy-grid-for-nav, .wy-nav-side { - top: 50px; + position: fixed; + top: 76px; + height: calc(100vh - 76px); } .wy-nav-side .icon-home { @@ -30,6 +28,11 @@ nav { background-color: var(--ns-navigation-background); } +.wy-nav-content-wrap { + height: 100%; + overflow: scroll; +} + .wy-nav-top { width: auto; max-width: none; @@ -51,17 +54,33 @@ nav { display: none; } +.wy-menu-vertical a { + color: currentcolor; +} + .wy-menu-vertical p.caption { color: var(--ns-navigation-foreground); } .wy-side-scroll { + width: 100%; + scroll-padding: 10ex; padding-bottom: 10ex; } +.wy-side-nav-search { + width: 100%; +} + +nav.stickynav { + padding-left: 0px; + padding-right: 0px; +} + .wy-nav-content { padding-top: 3ex; max-width: 100%; + height: auto; } .wy-breadcrumbs { @@ -76,3 +95,29 @@ nav { hr { clear: both; } + +a { + color: currentcolor; + text-decoration-line: underline; +} + +a:visited { + color: currentcolor; +} + +a:hover { + color: var(--ns-color-black); +} + +.wy-breadcrumbs a { + text-decoration-line: none; +} + +.d2 div.md { + background-color: lavender; + border: 2px solid purple; +} + +.text-italic tspan { + font-style: italic; +} diff --git a/doc_theme/js/scroll_navigation.js b/etc/doc_theme/js/scroll_navigation.js similarity index 100% rename from doc_theme/js/scroll_navigation.js rename to etc/doc_theme/js/scroll_navigation.js diff --git a/etc/eslint.config.js b/etc/eslint.config.js new file mode 100644 index 00000000..1847481c --- /dev/null +++ b/etc/eslint.config.js @@ -0,0 +1,89 @@ +import globals from 'globals' +import jslint from '@eslint/js' +import tslint from 'typescript-eslint' +import pluginVue from 'eslint-plugin-vue' +import vueParser from 'vue-eslint-parser' + +export default tslint.config( + jslint.configs.recommended, + ...tslint.configs.recommended, + ...pluginVue.configs['flat/recommended'], + { + files: ['**/*.vue', '**/*.js', '**/*.mjs', '**/*.ts'], + languageOptions: { + ecmaVersion: 'latest', + }, + rules: { + '@typescript-eslint/no-unused-vars': [ + 'error', + {argsIgnorePattern: '^_'}, + ], + '@typescript-eslint/no-empty-function': [ + 'error', + {allow: ['methods']}, + ], + 'max-len': ['error', {code: 80, comments: 80}], + 'no-trailing-spaces': 'error', + 'no-undef': 'error', + 'operator-linebreak': [ + 'error', + 'before', + {overrides: {'=': 'after'}}, + ], + }, + }, + { + files: ['**/*.vue'], + languageOptions: { + parser: vueParser, + parserOptions: { + parser: tslint.parser, // parse TS inside VUE + }, + }, + rules: { + 'vue/html-closing-bracket-newline': [ + 'error', + { + singleline: 'never', + multiline: 'never', + selfClosingTag: { + singleline: 'never', + multiline: 'never', + }, + }, + ], + 'vue/html-indent': ['error', 4], + 'vue/max-attributes-per-line': [ + 'error', + { + singleline: {max: 25}, + multiline: {max: 2}, + }, + ], + 'vue/multi-word-component-names': 'off', + 'vue/singleline-html-element-content-newline': 'off', + // For the Paramable interface, v-model directives + // need type annotation + 'vue/valid-v-model': 'off', + }, + }, + { + files: ['src/components/MageExchangeA.vue'], + rules: {'max-len': 'off'}, + }, + { + files: ['etc/vitest.config.ts'], + languageOptions: { + globals: { + ...globals.node, + }, + }, + }, + { + files: ['src/visualizers/p5.brush.d.ts'], + rules: {'no-undef': 'off'}, + }, + { + ignores: ['.venv/*', 'dist/*', 'e2e/results/*'], + } +) diff --git a/mkdocs-workbench.yml b/etc/mkdocs-workbench.yml similarity index 100% rename from mkdocs-workbench.yml rename to etc/mkdocs-workbench.yml diff --git a/mkdocs.yml b/etc/mkdocs.yml similarity index 52% rename from mkdocs.yml rename to etc/mkdocs.yml index 198d04a2..6ad84044 100644 --- a/mkdocs.yml +++ b/etc/mkdocs.yml @@ -4,54 +4,73 @@ nav: - Overview: README.md - User Guide: - Introduction: doc/user_guide.md - - ... | src/visualizers-workbench/* - - ... | flat | src/visualizers/* - - Extending: + - Visualizers: + - ... | src/visualizers-workbench/* + - ... | flat | src/visualizers/* + - Sequences: + - ... | flat | src/sequences/* + - Adding to Numberscope: - doc/extending.md - doc/running-from-source.md - - doc/making-a-visualizer.md - - Contributing: + - Building a Visualizer: + - doc/visualizer-overview.md + - doc/visualizer-basics.md + - doc/visualizer-in-depth.md + - Contributing your changes: - CONTRIBUTING.md - doc/onboarding.md - doc/working-with-pm.md - doc/working-with-git-and-github.md - doc/gitting-it-right.md - doc/code-principles.md - - doc/code-style.md + - doc/code-organization.md + - doc/code-tests.md - doc/pull-request-checklist.md - doc/visual-studio-code-setup.md - Internal code and APIs: - - doc/behind-the-scenes.md + - src/visualizers/VisualizerInterface.md - doc/working-with-bigints.md - ... | flat | src/shared/* - Other Information: - ... | flat | doc/*.md -docs_dir: doc -site_dir: dist/doc +docs_dir: ../doc +site_dir: ../dist/doc +validation: + nav: + omitted_files: info + not_found: info + absolute_links: relative_to_docs + links: + not_found: warn + anchors: warn + absolute_links: relative_to_docs + unrecognized_links: warn theme: name: readthedocs - custom_dir: doc_theme/ + custom_dir: doc_theme prev_next_buttons_location: both sticky_navigation: false -extra_css: [css/coordinate.css, css/override.css] +extra_css: [doc/css/coordinate.css, css/override.css] extra_javascript: - js/scroll_navigation.js - - https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML + - https://cdn.jsdelivr.net/npm/mermaid@11.4.0/dist/mermaid.min.js + - https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-chtml.min.js plugins: awesome-pages: collapse_single_pages: true filename: mkdocs-index.yml + d2: {} search: {} semiliterate: - exclude_extensions: + copy: true + exclude: - '.jpg' - - '.ttf' - '.ico' - '.spec.ts' - 'LICENSE.md' - ignore_folders: [node_modules, dist, coverage, '.venv', '.git'] - ignore_hidden: false - include_extensions: ['.png', 'math.ts', '.yml'] + - '~' + ignore: [node_modules, dist, coverage, '.venv', '.git', tests, e2e] + include: ['.png', 'math.ts', '.yml', '.svg', '.ttf'] extract_on_copy: true extract_standard_markdown: terminate: '' @@ -60,9 +79,11 @@ plugins: - pattern: '\.ts' # Standard in-code markup extract: start: '^\s*/\*\*+\W?md\b' # Opening like /** md - stop: '^\s*\*\*/\s*$' # closing **/ by itself on a line + stop: '^\s*(?:/\*\s*)?\*\*/\s*$' # closing **/ - pattern: '[.](.*).generator$' destination: '\1' markdown_extensions: - - pymdownx.superfences + - def_list + - markdown_grid_tables - mdx_math + - pymdownx.superfences diff --git a/etc/prettier.config.js b/etc/prettier.config.js new file mode 100644 index 00000000..61b9c23a --- /dev/null +++ b/etc/prettier.config.js @@ -0,0 +1,19 @@ +export default { + arrowParens: 'avoid', + bracketSameLine: true, + bracketSpacing: false, + embeddedLanguageFormatting: 'auto', + htmlWhitespaceSensitivity: 'css', + insertPragma: false, + jsxSingleQuote: false, + printWidth: 78, + proseWrap: 'always', + quoteProps: 'as-needed', + requirePragma: false, + semi: false, + singleQuote: true, + tabWidth: 4, + trailingComma: 'es5', + useTabs: false, + vueIndentScriptAndStyle: true, +} diff --git a/vite.config.ts b/etc/vite.config.ts similarity index 71% rename from vite.config.ts rename to etc/vite.config.ts index 719c83ba..da5f7ec5 100644 --- a/vite.config.ts +++ b/etc/vite.config.ts @@ -8,7 +8,14 @@ export default defineConfig({ plugins: [vue()], resolve: { alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)), + '@': fileURLToPath(new URL('../src', import.meta.url)), + }, + }, + css: { + preprocessorOptions: { + scss: { + additionalData: '@import "@/assets/scss/_variables.scss";', + }, }, }, build: { diff --git a/etc/vitest.config.ts b/etc/vitest.config.ts new file mode 100644 index 00000000..721a6405 --- /dev/null +++ b/etc/vitest.config.ts @@ -0,0 +1,27 @@ +/// + +import {defineConfig} from 'vite' +import Vue from '@vitejs/plugin-vue' +import path from 'path' + +export default defineConfig({ + plugins: [Vue()], + test: { + globals: true, + environment: 'happy-dom', + exclude: [ + '**/node_modules/**', + '**/e2e/**', + '**/dist/**', + '**/cypress/**', + '**/.{idea,git,cache,output,temp}/**', + '**/{karma,rollup,webpack,vite,vitest,jest}.config.*', + '**/{ava,babel,nyc,cypress,tsup,build}.config.*', + ], + }, + resolve: { + alias: { + '@': path.resolve(__dirname, '../src'), + }, + }, +}) diff --git a/index.html b/index.html index 69c17a1d..399283e0 100644 --- a/index.html +++ b/index.html @@ -7,25 +7,11 @@ name="viewport" content="width=device-width, initial-scale=1.0" /> Numberscope - - - - - - - - - + + + + +