refs https://github.com/TryGhost/Toolbox/issues/207
- we want to start writing some E2E tests that involve automated
pointing and clicking around the frontend of Ghost to test that
members of Ghost sites can still do what we expect
- we've decided to look in to Playwright for this - it looks __really__
nice
- this is a VERY basic first test - it'll check for a 200 on the
homepage of whatever we provide as the TEST_URL env variable, or
default to a (manually-run) Ghost instance on port 2368
- also adds a `yarn test:browser` command to run the tests using the
Playwright CLI, and a sample GitHub Action workflow which we can
manually run with a site URL
- there's a lot more to add here in terms of test framework but this
gets us started
refs https://github.com/TryGhost/Toolbox/issues/214
- Calling `getMembersAPIAgent` and `getAdminAPIAgent` separately was booting Ghost twice, which caused a significant performance degradation.
- Additionally, having two calls was slightly ugly and having once utility function that delivers multiple agents at once feels like more readable syntax
- for some reason, this test seems to be failing now we've pulled it out
of the general CI
- it makes sense when the repo is clean, because the html files don't
exist, but I don't understand how they were working before... 🤔
- anyway, we should be overriding the path to the test fixtures admin
view files here
- this fixes the unit tests
- Often in our API we want to check that the location string looks roughly right for a resource
- At the moment we're matching any String, this upgrades the check to look for resource URLs
We've split the tests into two describes, one for when Stripe is enabled
and one without, because we setup Stripe in the before method.
We use nock to mock the Stripe server and assert that there is no call
to delete the subscription.
- I noticed our test fixture theme was still throwing errors during tests because it had partials that are no longer in use
- Updated the docs on how to update Casper to include ensuring Casper was on a valid version AND removing Casper before
copying it across
- Performed the exact steps in the README
- This ensures that the Casper version we use in our tests is not throwing tonnes of errors and is up to date
refs https://github.com/TryGhost/Team/issues/1338
This adds some initial scaffolding to make it easier to test Members with Stripe
- `mockStripe` method to disable the network, so we can use nock to intercept
Stripe API calls
- `setupStripe` method to be called _before_ getting an agent so that Stripe is
configured
- `getMembersAPIAgent` to get an agent for the Members frontend API
refs https://github.com/TryGhost/Toolbox/issues/209
refs https://github.com/TryGhost/Toolbox/issues/210
- Fixed request header processing by the e2e-framework where it failed to lowercase incoming header keys. This bug made it harder to test code paths which involve header checking, e.g. following code: `req.headers['stripe-signature']` would not get a correct value if the header was specified as Stripe-Signature
- Additional output to the status code assertions was added - this allows to have more context when an invalid response code comes back in a test.
no issue
- The members importer api tests don't use the e2e framework, so it's better to move them in their own file as we did for the e2e regression tests.
- Fixed a content-disposition header issue
refs 531ec579a2
- This test broke the build in commit 531ec579a2
- For some reason when running just the test file, it passes. When running the full e2e-api test suite, it fails.
- I'm commenting it to unblock the broken build and move on to other tests
refs: https://github.com/Ghost/Ghost/commit/b5ee17b25
- When moving the site tests into e2e-api they broke because they were getting the changed settings value from the settings test
- The solution is to ensure the settingsCache is reset between tests, which is what this commit does
- This commit also renames the shutdown method to reset, because this is not a permanent operation
- It also renames the resetDb method to resetData, because the concept is we want the internal data to be reset, not just the DB
- We're in the process of rolling out a new e2e-framework, and getting rid of the regression tests
- We'll eventually merge all the tests together and into using the same framework
refs https://github.com/TryGhost/Team/issues/1353
- Doing a renaming-only commit to avoid merge conflicts while I work on the new members.test.js test file
- The new `members.test.js` file will set the `multipleProducts` flag to true
no-issue
Because we were returning the call to `boot`, rather than awaiting it,
it meant that once a test was provided with an API Agent the Ghost
application hadn't necessarily started. This was noticed whilst working
on the Members API which requires the frontend to be loaded.
When the frontend is loaded we must also wait for the url service to
have finished initialising, so that we do not run into 503 maintenance
errors when hitting the frontend.
no-issue
The Members API is served on a different endpoint to the Admin API, and
also requires that the frontend is booted. This agent is to be used when
testing the Members API, e.g. Stripe webhooks or Members config.
- The admin API test agent doesn't boot the Ghost frontend, which is where members routes are currently located
- This means we can't test members properly as we don't have access to webhooks etc
- This change adds a members option to getAdminAPIAgent(), which in turn enables the frontend
- We do it this way so that we can easily change the implementation later, e.g. if we have the option to boot members directly
- When starting ghost for e2e tests we create a content folder in the os tmp dir
- This means that the folder can change between suites as ghost is started and restarted
- For the most part this is fine, but theme storage caches the path to config (which makes sense, it's not meant to change whilst Ghost is in-memory)
- This is a quick-n-dirty fix that just makes it possible to update that path in the tests, so we know it's in sync
- Ideally we'd not cache the path, use a function to fetch it etc, or fully reset the theme storage layer, but this is the fix I have working today
and so it's going in to unblock things for now
- This part of the framework can be used in isolation
- Using mockManager everywhere makes it more visible how to use it
- Aside: fixed .getAdminAPIAgent not needing a URL in site tests whilst cleaning up
- Can now easily mock labs flags as enabled or disabled using mockManager
- Updated some bits of code that directly mocked labs
- Aside: improved error thrown when things go wrong booting Ghost
refs daeb06e835
- This is an additive change, and the parameter is optional so it's better to ship it right away (testing was hard as it's a chicken and an egg problem to have a feature flag in the setup route).
refs: https://github.com/TryGhost/Toolbox/issues/158
- We only use chai in a handful of places now, and it seems totally unnecessary
- Use assert instead
- Made other minor changes with a view to this being a reference aka "perfect" test suite
refs: https://github.com/TryGhost/Toolbox/issues/158
- if we had to combine a large list of params then this would make sense
- we could/should also add a .query() method to our agent
- however, I don't think this is worthwhile/necessary for just 2 params right now :)
refs: https://github.com/TryGhost/Toolbox/issues/158
- continuing to turn this into a reference test suite for how to do things with the new framework
- use fixtureManager and mockManager properly
- testUtils should _not_ be required anymore!
- TODO: cleanup labs mocking and side effect assertion using models
refs: https://github.com/TryGhost/Toolbox/issues/158
- rather than just exposing any, anything and string matching, expose more specific matchers.
- this was triggered by `any(Date)` not working for dates in our API
- it seems poor to match `any(String)` for something we want to be a well formatted date
- establishes the pattern of using our defined matchers instead of requiring any/anything from jest
refs: https://github.com/TryGhost/Toolbox/issues/158
- Working up to having these be the defacto "reference" tests
- Changing this allows for the fact that there will be a getContentAPIAgent, and probably more in future
- Abstracting away the path to a single location will make updating API paths easier later too
refs TryGhost/Toolbox#158
refs TryGhost/Toolbox#129 (comment)
We now have @tryghost/jest-snapshot - a suite of utilities for using snapshots with mocha, should, etc
The @tryghost/express-test lib also uses it to provide chained snapshot assertions
This library was created because all the existing implementations I could find, including the in use @ethanresnick/chai-jest-snapshot
didn't properly support property matching. @ethanresnick/chai-jest-snapshot supposedly supported it, but the implementation was incorrect
and frequently lead to false test passes.
This library also has (after some back and forth) path resolution so that snapshots can live in a local __snapshots__ folder
refs a3cc66be50
- in the referenced commit, I made a util to speed up resetting the DB
for SQLite by copying the database file
- I inadvertently removed an optimization we had before - where we
truncate the tables and insert the fixtures instead of dropping the
entire database
- this would be missing on MySQL tests
- this seems to have a big difference so this commit re-adds the
optimization in
The idea here is to keep the concept of _what_ we're asserting (that an email was sent)
away from the implementation of how we check that assertion
This means that tests can have a consistent api like .sentEmail(), and if we change how that function works,
we only have to update the assertion function in the MockManager
Much more cleanup to come behind the scenes, but the aim is to make the tests as clean as possible
- encapsulated concerns within individual objects
- this will allow us to refactor these into classes or move them around later
- also makes it clearer how methods like restore relate to other methods
- e.g mocks.restore() restores mocks, whilst fixtureManager.restore() restores the database
- Ensure that the DB is always reset between tests
- We assume that the DB will be torn down and rebuilt between tests
- Without this, previous tests clobber the current tests, and it's not possible to tell when running files in isolation
which makes developing really tricky
loses TryGhost#13971
This fixes an issue with links containing # anchor. It makes sure the # part is at the end of the url like url?v=hash#anhor
Co-authored-by: Hannah Wolfe <github.erisds@gmail.com>
refs https://github.com/TryGhost/Team/issues/1296
- The `theme` must be a github `org/repo` string
- This uses the internal API instead of the services because the API has extra implementation details not present in the services.
- This is so that we can start refactoring more tests to use our new e2d framework
- That framework doesn't support file uploads yet, so tests with uploads are split out
refs https://github.com/TryGhost/Team/issues/1071
Default content visiblity for specific tiers is now stored split between `default_content_visiblity` and `default_content_visibility_tiers` setting, with former storing the value as `tiers` and the latter stores the list of tiers that the visibility is restricted to. This migration transforms all existing sites that have default visibility stored as an NQL string from previous versions to follow the new model and store correctly on the new setting.
refs https://github.com/TryGhost/Team/issues/1071
Default content visibility for a post can be one of `public|members|paid|tiers`, where `tiers` denotes visibility restricted to specific tiers. This change adds a new setting to store the tier ids when default content visibility is set to `tiers`. This closely matches how the visibility is stored on `posts` table as well, with `visibility` stored as `tiers` and tiers data is stored on tiers pivot table.
refs https://github.com/TryGhost/Team/issues/1287
Currently we have a hard limit of how large an email filter can be,
which is very restrictive once a site starts using Tiers - by moving
toward a TEXT column, we essentially give the filters unlimited size.
This currently doesn't handle SQLite as there are no limits on VARCHARS
in SQLite.
The down migration is a loop so we don't have to handle values larger than
50 characters
no-issue
refs https://github.com/TryGhost/Members/commit/6860e3c1
refs https://github.com/TryGhost/Members/commit/03a6d694
This adds an e2e tests for the regression we had with comping members.
The stripe service imports the members service, which will instantiate
members-ssr once it is loaded, which will use the uninstantiated
settings cache resulting in errors due to a missing theme_session_secret.
For this reason we require the stripe service inside of the test function,
rather than at the top level of the file
refs TryGhost/Team#1071
- new `tiers` key is now attached to posts/pages API response to include tiers visibility
- updates expected response for post/page in tests to include `tiers`
refs https://github.com/TryGhost/Team/issues/1071
We used `posts.visibility` originally to store visibility as `free|paid` with a character limit of 50. This same field was repurposed to store an NQL filter when member tiers is enabled. The NQL filter uses the slug of the tier name, which can easily create a filter longer than 50 characters, adding an unwanted limitation on number of tiers that can be added to post's visibility.
Going forward, we'd like to store the visibility of posts for tiers in a separate pivot table and instead store the value of `visibility` as `tiers` when restricting post access to specific tiers. This change -
- adds a new pivot table fixture for storing relation between posts and tiers
- adds a migration for creating the new table
- updates tests
refs https://github.com/TryGhost/Team/issues/1168
Rather than using a single url for paid signup redirects, we want to
support setting a welcome page on a tier by tier basis. This column will
be used to store the URL. A text column of length 2000 is how we have
stored URL's elsewhere in the schema.
closes: https://github.com/TryGhost/Toolbox/issues/193
- Our Casper fixture was several years out of date.
- I'd already updated the ghost-api to point to the latest API version, which was the main difference
- This makes sure the full fixture is up to date and correct, and therefore that we're truly testing if Ghost right now works
- It also adds instructions for how to update it in future
GOTCHA: the mock-express-style tests are failing if the API difference between test-theme and casper are different
- I've tried to look into why this is - it's something to do with the overridden route settings not working properly if the API version changes
- Given that we may not keep this style of testing AND we are definitely not keeping API versions, I'm ignoreing this for now
- To get around it, I'm setting both themes to be v4 API, so that Casper is exactly as in main
refs: https://github.com/TryGhost/Toolbox/issues/193
- The Casper fixture in the codebase is copied to a tmp folder structure every single time we start Ghost in an e2e test
- We shouldn't keep files here that don't actually get used!
- The files I'm removing are src files, used for building, but we already have the built files
refs: https://github.com/TryGhost/Toolbox/issues/168
- Upgraded the remaining themes to be pinned to the canary API
- This required one minor fix because the edit URL has changed for authors in v4/canary
- I also ripped out everywhere that the theme ghost-api version was being pinned to canary, as this was unreliable and only happening in a few places
- It's also going to be unnecessary code as soon as we finish changing to only having one API version
- This test failed for me intermittently because the posts would be out of order
- I assume this is due to my super-powered M1 mac 😂
- This rewrite only aims to remove the dependency between the insertion order and the output order
- Everything else should be the same, and it still tests that the posts that are meant to be members only are exactly that
refs: https://github.com/TryGhost/Toolbox/issues/168
- The fixture represented Casper at Ghost version 1.0 which pre-dated the introduction of the posts_per_page config in package.json
- When Casper was upgraded to 2.0 in the fixtures, the lack of pagination broke the e2e tests for pagination
- This change introduces proper code to stub and override posts_per_page rather than keeping and using the old casper-1.4 fixture
- It also required me to remove a handful of CSS-based checks which are no longer true in the new theme version, but also didn't really add anything to the tests
no-issue
This test should have always been a unit test, but it now no longer
serves a purpose, as we do not rely on the event being emitted - it
would also not break Ghost if the event _was_ emitted, so we should not
be testing for that.
refs: https://github.com/TryGhost/Toolbox/issues/168
- Having large theme fixtures makes tests run slower, so we're working to reduce them
- This fixture was a full copy of a very old version of Casper, but all we needed was a handful of files so that the tests can run
- This theme is also used for testing members, not just price data so I've renamed it for clarity
- The remaining files is the bare minimum we need to test these features
refs: https://github.com/TryGhost/Ghost/issues/10434
refs: https://github.com/TryGhost/Ghost/pull/10449
refs: https://github.com/TryGhost/Ghost/pull/10559
- We originally had a weird structure returned for data keys in routes.yaml.
- To the best of my knowledge this was never desired or really used
- I'm removing it now simply because I'm trying to remove all references to v2/v3 in tests, and this had a comment saying it was deprecated in v3
- I could have changed the comment to be a proper @deprecated comment and leave this til we rewrite dynamic routing
- However it's weird and confusing and I believe entirely unused - so getting rid is way way better
refs: https://github.com/TryGhost/Toolbox/issues/168
- This file was pinning various tests to v3
We're going to be dropping the idea of having multiple versions of the API in each Ghost version.
Because this has not achieved the goal of making it easier to make breaking changes, but it has
created an ordinate amount of technical debt and maintenance overhead.
As we know this is going away in the next major, there is no benefit to us constantly running tests
that check if those versions still work, especially given how long they take.
Instead we're starting work to ensure that all of our test work on canary, and that canary has
excellent test coverage so that we can be sure that our one API version works really well and that
any changes, no matter how subtle are deliberate, tracked and understood.
refs: https://github.com/TryGhost/Toolbox/issues/168
- All of our unversioned tests should be running against canary already
- These tests are erroneously running on the wrong version
We're going to be dropping the idea of having multiple versions of the API in each Ghost version.
Because this has not achieved the goal of making it easier to make breaking changes, but it has
created an ordinate amount of technical debt and maintenance overhead.
As we know this is going away in the next major, there is no benefit to us constantly running tests
that check if those versions still work, especially given how long they take.
Instead we're starting work to ensure that all of our test work on canary, and that canary has
excellent test coverage so that we can be sure that our one API version works really well and that
any changes, no matter how subtle are deliberate, tracked and understood.
refs: https://github.com/TryGhost/Toolbox/issues/168
- These are all places where we reference an API version like v2 or v3 but it's not actually
used or relevant.
- The aim is to get rid of all mentions of these old versions to make it clearer that we're only running tests on canary
refs: https://github.com/TryGhost/Toolbox/issues/168
- These are all test files I missed in ffcd3fbe313b4a413833da9a7473376cb21246fd
We're going to be dropping the idea of having multiple versions of the API in each Ghost version.
Because this has not achieved the goal of making it easier to make breaking changes, but it has
created an ordinate amount of technical debt and maintenance overhead.
As we know this is going away in the next major, there is no benefit to us constantly running tests
that check if those versions still work, especially given how long they take.
Instead we're starting work to ensure that all of our test work on canary, and that canary has
excellent test coverage so that we can be sure that our one API version works really well and that
any changes, no matter how subtle are deliberate, tracked and understood.
refs: https://github.com/TryGhost/Toolbox/issues/168
We're going to be dropping the idea of having multiple versions of the API in each Ghost version.
Because this has not achieved the goal of making it easier to make breaking changes, but it has
created an ordinate amount of technical debt and maintenance overhead.
As we know this is going away in the next major, there is no benefit to us constantly running tests
that check if those versions still work, especially given how long they take.
Instead we're starting work to ensure that all of our test work on canary, and that canary has
excellent test coverage so that we can be sure that our one API version works really well and that
any changes, no matter how subtle are deliberate, tracked and understood.
- This test failed after updating Casper, because Casper has changed how it outputs next/previous type posts at the bottom
- This test was overly specific. If we want to test if the next and previous posts are output, we should have a particular assertion/test that clearly does that
- I've changed the test to not use cheerio and just do a couple of checks to make sure that head and body contain something sensible
- This will still be specific to the fixtures though
refs https://github.com/TryGhost/Team/issues/1252
We need a way to signal whether or not a Tier is active or archived, and
we'll be using the active flag in the same way we do for Offers.
fixes https://github.com/TryGhost/Ghost/issues/13832
The `date` handlebars helper has only one option currently — `format`. It assumes the locale and timezone from the `options.data.site` object which is not always desired behavior.
The helper sometimes is used, for example, in custom RSS template where we always need the `en-US` locale, not the one that we have configured for the website globally. This change makes the two options configurable, and defaults to the `options.data.site` object values, if not specified in the helper (keeps the backwards compatibility with the current behavior).
refs https://github.com/TryGhost/Team/issues/1257
Offer Redemptions were being overcounted due to the way we were updating
Stripe configuration for the Members service. We would create a new
instance of the members-api, which would have event handlers for
creating Offer Redemptions - by creating a new instance each time Stripe
config changed, we would overcount them.
Here we've pulled out Stripe related logic into the Stripe service, and
updated it internally - rather than creating a new instance. This means
that we've been able to remove all of the logic for re-instantiating the
members-api.
- Bumped members-api & stripe-service
- Removed reinstantiation of members-api
- Used stripe service to execute migrations
- Updated Stripe Service to handle webhooks & migrations
- Used webhook controller from stripe service
- Used disconnect method from stripe service
- Removed unused stripe dependency
- Removed Stripe webhook config from members-api
Since we now have 2 products by default for all ghost sites, free and default paid, the usage of default product which so far was using first product needs to be updated to use the first paid product.
- updates default product usage to use first paid tier
- updates tests
refs https://github.com/TryGhost/Team/issues/1189
Support for AMP is slowly in decline, and makes developing new cards trickier,
since AMP no longer has an effect of SEO we're going to disable it by default
as a first step toward moving away from it.
Co-authored-by: Thibaut Patel <thibaut@ghost.org>
closes https://github.com/TryGhost/Zapier/issues/56
- fixes tag creation when creating posts with `tags: [{slug: 'new'}]` which should be supported
- assigning tags with only `{slug: 'new'}` was triggering our validation for the required `name` property then bubbling up to the `bookshelf-relations` library resulting in a 500 error
- the fix applied here is to set the `name` field to the same as the `slug` field if a name is not provided
no-issue
These tests were incorrectly checking for a subscribed value of true,
and thus failed to catch the bug fixed in the previous commit. The tests
now reflect the intended behaviour.
refs: https://github.com/TryGhost/Toolbox/issues/150
Instead of loading all themes for each set of tests in the e2e suite, only load the frontend at all for frontend tests, and only load themes for the theme tests.
closes https://github.com/TryGhost/Ghost/issues/13920
- Custom excerpt should be used as a fallback for meta_description in line with the behaviour of OG and Twitter metadata
- We specifically don't want to use the full fallback to the auto-generated preview text when a custom excerpt isn't defined, because we trust search engines to be able to summarise content better than we can
closes https://github.com/TryGhost/Team/issues/1164
- `customThemeSettings` feature is GA so any conditionals can be cleaned up
- removed conditional loading of custom theme settings and associated API routes
- removed event trigger for reloading custom theme settings when the feature flag is toggled
- removed flag from labs GA list
refs https://github.com/TryGhost/Team/issues/1216
Some email security clients are scanning links at delivery, rather than
at the point the user clicks on them. This is causing magic links to
expire. To get around this we're increasing the grace period in which a
link can be used multiple times to 10 minutes.
refs https://github.com/TryGhost/Team/issues/1251
With sites that have a huge number of resources, using limit="all" can
cause OOM errors at the Node level. Administrators now have the ability
to cap limit="all" requests via config. This only affects the get helper
used in themes, not the API, this is by design as themes have less
visibility of issues.
refs https://github.com/TryGhost/Toolbox/issues/158
- To make the development quicker the copy-paste code was left in the codebase but gave immediate smell. Removed it to stop bothering me every time I look at the related code :)
refs 3150c87935
- Adds basic coverage for a bug that was fixed in referenced commit.
Next time it should be easier to add more tests to the suite as there's
already an example starter to work your way from.
refs https://github.com/TryGhost/Toolbox/issues/129
- The "Origin" header is require in Admin API requests so it makes sense to bake it into a default request made by most tests. Reduces unnecesary fluf around test request setup and removes a "config" dependency in each tests suite using the "e2e framework"
refs https://github.com/TryGhost/Toolbox/issues/158
- Allows for much smaller amount of code to configure a test to work with chai-jest-snapshots. They now work automatically for all regression tests and could be enabled for other suites by adding the "--require=./test/utils/snapshots.js" parameter in respective test:* package script
- Regenerated snapshot for authentication test as the naming structure
changed a little with the snapshot metadata being taken on a higher
level in the test (uses the suite name instead of a specific describe it
used to be called from)
refs https://github.com/TryGhost/Toolbox/issues/158
- Moving common mocking/stubbing/spying logic into an outside utils module allows test suites to keep agnostic towards which framework powers mocking etc. Should also substitute email service stubbing used in multiple places
- Bonus, got rid of should dependency, which is deprecated
no issue
- The response from the server might not always contain an array with errors (for example when there's a generic 503 error). This changes prevents tests from hanging and adds at least some relevant output
refs https://github.com/TryGhost/Toolbox/issues/129
- Adds content folder creation based on fixtures for e2e tests using the e2e framework
- Mostly a copy-paste for now from existing e2e utils with some simplifications, so we can "complicate things" later only if needed
refs https://github.com/TryGhost/Toolbox/issues/129
- This is an example of how the converted test suite syntax could look like when using jest snapshots.
- The tradeoff is not that visible just yet as these tests were mostly checking few fields, but when the whole range of admin API tests is convered we'll be able to get rid of the "checkResponse" utiliti methods along with all the supporting luggage!
refs 24505db918
- The code is heavily borrowed from the referenced commit by @ErisDS. It starts a fresh abstraction layer for all e2e tests to use, provides simle interface for request agent initialization and state management (read the e2e-framework top file description for more)
- Main methods the framework exposes are:
- getAgent - to initialize request agent with a fresh Ghost instance
- initFixtures - to initializer a fresh db state
- resetDb - cleans up the db state if there were state manipulations during the test suite run (POST, PUT requests or non-default fixtures)
refs https://github.com/TryGhost/Toolbox/issues/129
- Having a fresh module should allow building new testing utility concepts from the ground up without being tied by the mystery baggage of the legacy localUtils/testUtils
- The outline of the concepts the framework will be handling is in the commont on the top of the e2e-framework file
refs https://github.com/TryGhost/Toolbox/issues/152
- This refactor fixes the rest of authentication test suite and outlines the state setup steps that are absolutely needed for tests that rely on any db state. It's still counterintuitive why the fixture initialization has to be called and in that specific order, so next will be refactoring in this area to simplify the initi code
refs https://github.com/TryGhost/Toolbox/issues/152
- Passing around plain options object tends to become quite unreadable long term. While these new utils are being shaped up it's still easy to change interface and introduce new parameters with time as needed.
refs https://github.com/TryGhost/Toolbox/issues/152
refs 5bea089dfe
refs 16ad5f73c4
- The refactor is heavily inspired by the referenced commit from @ErisDS
- This refactor is meant to serve as a template for further refactors and unification of the abstraction over "request". Should allow us to be more agnostic towards the library that's powering the request thing. For example, mock-express style of request handling could substitute or get substututed easily if all tests are behind similar "get(Authenticated)Agent" interface
refs https://github.com/TryGhost/Toolbox/issues/152
- This is a continuation of an experiment to switch over to serverless boot in regression tests. Just a proofe of concept that authentication scenarios would also work and the expectations don't collapse unexpectedly.
- Nothing too crazy so far, easy and straight forward substitution of the config "url" with an express app passed to the supertest agent
refs https://github.com/TryGhost/Toolbox/issues/152
- Being able to set up test suites without blocking a port opens a door to new ways to run tests - for example this has been one of the blockers for running mocha tests in parallel
- Additional benefit is lighter statrup, which reducec the test execution time slightly. Doesn't seem like much but these things stack up!
no issue
- we've seen some instances of SQLite saying "database disk image is malformed"
- I think this happens because we copy the file whilst we are still connected to
the old DB
- this commit destroys the connection before copying the clean file to the live
DB file
refs https://github.com/TryGhost/Toolbox/issues/136
- we nuke and reinitialize the DB many times between tests
- this forms a good portion of the time taken and we shouldn't be spending
so much time on just resetting the DB back to a known state
- this commit switches out the knex-migrator reset + init calls for SQLite to
a function which keeps a clean copy of the DB and copies the file back
when we "reset"
- for MySQL, existing functionality is kept
- this massively speeds up tests because it saves ~700ms+ for every reset
- whilst this change seems to work, it's just the start so there's a
lot more refactoring needed. this change is currently gated to CI until
it's deemed safe/sane enough to run on local machines without blowing
up
refs https://github.com/TryGhost/Toolbox/issues/152
- Have skimmed through the test suites in hopes to find some quick performance wins to bring the runtime speed closer to the one in "main". Haven't been successful to identify major wins, cleaned up a couple of small bits.
- We'll have to live with a tradeoff between maintainability/unified boot VS cost of mainitaining a fake boot process. Imo extra couple seconds of runtime is worth it.
refs 3c7a8dead4
- The tests needed adjustments with the native boot mechanism.
- The number of returned posts changed in the test resutls because duing native boot we also insert fixtures which add to the number of initial posts
- The vhost regression suite is still failing and I had no strength to figure out why. The redirect it fails with makes no sense, the clue here is that the test doesn't fail when running in isolation, so probably has to do with some leftover overrides from the previous test cases.
refs 3c7a8dead4
- The boot process has been using an asyc method to load the routes file, which is the case now for these tests since the switch to raw boot method instead of mimicking it manually
refs 3c7a8dead4
- Simplifies the state initialization code significantly and reuses native boot mechanism instead of mimicking it (it was a headache to maintain with all the internal services moving around)
refs ec28478435
- the method signature changed in
9fa8800b9d
and the usage wasn't updated in Ghost
- this commit updates the tests to reflect this internal change
refs https://github.com/TryGhost/Team/issues/960
- Character like "%%" or "%80" would crash our current url escaping behavior. We consider they aren't valid URLs as the percentages haven't been properly escaped.
refs https://github.com/TryGhost/Team/issues/1200
- The leading/trailing whitespaces are trimmed by `new URL()` but are considered invalid in metascraper. Trimming solves this edge case.
refs: TryGhost/Toolbox#147
* Replaces all references to isIgnitionError with isGhostError
* Switches use of GhostError to InternalServerError - as GhostError is no longer public
There are places where InternalServerError is not the valid error, and new errors should be added to the @tryghost/errors package to ensure that we can use semantically correct errors in those cases.
refs https://github.com/TryGhost/Team/issues/1237#issuecomment-981770688
- API key names for external services now follow a standard pattern
- top-level key of the service name
- public/private and read/write perms inside the name, eg. `publicReadOnlyApiKey`
- updated test to match expected API key name
closes https://github.com/TryGhost/Toolbox/issues/148
- These regression tests introduce very little additional value in exchange for an expensive time to run them. Because we mostly care about stable support for the latest stable API version, the older API version test for "internal" API can go away
- In case there are bugs found we do care about in the v2/v3 APIs we can always revert some of these tests.
https://github.com/TryGhost/Toolbox/issues/140
- This test was bloating the regression/site suite and was using hacks (calling the Admin API) to create a custom redirects state
- It suits way better in e2e frontend test suite with less hacky approach to start the Ghsot instance - using custom routes file path to initialize the instance
https://github.com/TryGhost/Toolbox/issues/140
- Allows to fully start an instance with a custom routes.yaml file without a need to do workarounds - e.g. we used to call an internal Admin API from a regression test suite, which is not a good practice
- Providing "routesFilePath" to startGhost method copies that file to the default settings location and start the instance with that configuration. No need to do API calls or check if the routing service "isFinished"
refs https://github.com/TryGhost/Toolbox/issues/140
- Same/similar RSS tests were present in regression and e2e tests suites. It made sense to move missing cases from regression to e2e. This saves us time bootstraping db state in multiple places and keeps all test cases regarding single feature in same place
refs https://github.com/TryGhost/Toolbox/issues/139
- With ec2aed5ce8 the DynamiRedirectsManager has reached 100% test coverage and most of the tests present in the removed regression suite have been ported to unit tests
- No need to keep slow tests around! :)
refs https://github.com/TryGhost/Toolbox/issues/139
- The regression test suite for redirects functionality for way too big. And each restart was causing massive overhead. It's enough to have a single exhaustive test using multiple input files
- The tests testing API endpoints should've been e2e tests to start with
- The rest is covered in the unit tests for redirects api service
refs https://github.com/TryGhost/Toolbox/issues/139
- Having tight coupling with backup file path calculation for redirects makes it extremely hard to test. In addition, having it injected will make it easier to swap this dependency to the mechanism similar to one used for routes files
refs 91efa4605c
- Referenced commit introduced a double json-stringification to uploaded redirects.json files.
- The endpoint has no stability index of any sort and is meant to be dropped in Ghost v5. It's best to rework the redirects to the yaml format as descirbe here - https://ghost.org/docs/tutorials/implementing-redirects/#file-structure
- moving this middleware because we're about to add a second piece of middleware
- it's easier to see what we have when each middleware is in its own file rather than in one big middleware.js file
no issue
- this commit adds a counter for the number of boots we do in tests
- which therefore allows us to calculate the average boot time we
experience
- only useful for debugging test performance
refs https://github.com/TryGhost/Toolbox/issues/139
- The v2 and v3 redirects APIs are unofficial and should not be used by anyone in production. There's no good reason to maintain expensive to run test suites for old unofficial APIs.
- The test cases in canary suite covers the functionality of redirects enough to be sure they work as expeted
refs https://github.com/TryGhost/Toolbox/issues/138
- These tests still rely on the frontend to be present. Needs further investigation to remove "frontend: true" flag - it slows down test runs!
refs https://github.com/TryGhost/Toolbox/issues/138
- Final batch of the refactor to async/await syntax. Doing these refactors before modifying "testUtils.startGhost" everywhere to boot only with the backend
refs https://github.com/TryGhost/Toolbox/issues/138
- This is a continuation of a bigger refactor to use async/await syntax before migrating "startGhost" methods to only use backend boot
- Removed a little bit of dead code (like admin user creation) which should speed up test execution too!
- Refactored user variables to be declared closer to their usecases instead of being high up in a global scope - variables shoul not live that far apart from the code that uses them
refs https://github.com/TryGhost/Toolbox/issues/138
- First batch of the refactor to async/await syntax. Next one will cover the rest. Doing these refactors before modifying "testUtils.startGhost" everywhere to boot only with the backend
refs https://github.com/TryGhost/Toolbox/issues/138
- There is no good reason to keep this extra variable around just call "stop" in couple very specific cases. Even for those cases, there's `testUtils.stopGhost` method which achieves the same without additional variable to track.
refs https://github.com/TryGhost/Toolbox/issues/138
- Having the "ghost" alias only added cognitive load when reading through the test code and didn't provide any additional value. Removed the pattern to keep things simpler and more explicit
refs https://github.com/TryGhost/Toolbox/issues/138
- Using asycn/await syntax is way more readable and allows to identify further reusable patterns in test initialization. This refactor also served as an exploreation around how the code looks like at this point
refs: 2af9e2e12
- This new HTMLErrorRenderer is borrowed heavily from finalHandler
- This is the module that express uses to render errors if there is no custom errorhandler
- It just renders a really simple html page wrapping err.stack in a <pre>
- This results in a nicely formatted, but unstyled error page
- I also updated BasicErrorRenderer to use the same res.statusCode + err.stack pattern rather than err.message
Note: This error renderer is _only_ used for renderering errors on the `/ghost/` route
- In almost all cases, errors here are rendered by Ember
- The only error that can be rendered here is a missing template error see: 2af9e2e12
- Card asset reloading was incorrectly only happening if the API version changed 🙈
- In addition, having an init function was redundant, as theme activation happens on boot
- This meant that the card assets were being generated twice on boot
- Instead, we now only generate them on theme activation, which covers the boot case and simplifies all the logic
- Currently it's assumed that public files are 100% static
- With card assets, we're using it for files that are partially static, but can change between reboots and theme changes
- We already have a system for managing cache busting across theme changes and restarts - the ?v= key that is added via the asset helper
- This was already in place and used, but servePublicFile's internal cache didn't honor this key, and cached for the lifetime of boot
- This small change means that if a ?v= query param is present on a request for a public file, that we pay attention to it. Else we cache as before
refs https://github.com/TryGhost/Toolbox/issues/135
- Looking closer into the reason why the test was failing without "forceStart" revealed that the server only start was overoptimized - "initServicesForFrontend" should be a part of a backend as those are generic theme services.
no issue
- Having rewire here doens't do any difference and should not be used if absolutely needed. Usually using rewire gives a code "smell" so there's some sort of coupling that's going on and probably has to be addressed first
refs https://github.com/TryGhost/Toolbox/issues/135
- The reason the test **settings** test was failing when the force start flag was removed in the **custom themes** was the bridge! The bridge was trying to execute function on the frontend when the boot was done without initializing the frontend. The setting test was changing locale and the timezone which triggered events calling up on frontend components - we clearly don't want to do this when the instance is booted without the frontend
- To make event initialization conditional moved it to the "init". This way the event listeners are only set up when we boot with the "frontend" flag set to true
refs https://github.com/TryGhost/Toolbox/issues/135
- Without sensible defaults the web app was not initializing either the backend nor the frontned parts of the application. Fixed the defaults so the problem doesn't happen again and optimized mock-express-style initialization to only initialize the frontend routing
refs https://github.com/TryGhost/Toolbox/issues/135
- This optimization is expected to play a role in more consistent "backend-only" boot where the previous test state might have left over a different theme version which might cause in unwanted URL Services reainitializations.
- What has been happening here is the themes.test.js suite was uploading a theme with a v4 api and when the users api test suite loaded up it switched back to a default v2 theme, which caused routing reinitialization
- The root problem here is the themese suite is leaving a mess behind so a "restartModeGhostStart" is not really possible anymore - this should be cleaned up separately
refs https://github.com/TryGhost/Toolbox/issues/135
- Allows to turn off overwriting urls/resources JSON file caches on testing environment. This is needed to have predictable state when running multiple test suites that stop the Ghost process and try to persiste URL cache.
refs https://github.com/TryGhost/Toolbox/issues/135
- This extracts the file storage knowledge out of the URL Service an allows to have optional features based on the environment - for example turning off writing cache for when running tests
refs https://github.com/TryGhost/Toolbox/issues/135
- The global default should stay the same as it used to be and we can introduce an override for "withFrontend:false" on casa-by-case or area-by-area bases
refs https://github.com/TryGhost/Toolbox/issues/135
- Going though local utils allows to have the "withFrontend: false" flag applied only to the e2e-api test suites. This way we can gradually introduce the no-frontend change across all test suites
refs https://github.com/TryGhost/Toolbox/issues/135
- Having the proxy in local utils allows to disable frontend gradually in test suites instead of all at once (the latter approach didn't work out as too many other suites still depend on full frontend initialization)
refs https://github.com/TryGhost/Toolbox/issues/135
- When running e2e-API test in most cases there's no need to boot Ghost instance with full frontend. This should improve the boot time which should reflect on the speed of running test suites
- The tests where the "forceStart" and "withFrontend" are used together indicate that there's still some work to do to fully separate frontend/backend boot line. The force start is also unnecessary, but was needed to reinitialize all services properly - should be investigated!
refs https://github.com/TryGhost/Toolbox/issues/135
- To be able to reliably start ghost instance without a frontend the process needs access to urls/resources caches
- Storing the configuration in "paths" for now as there's no better place for it untill we are able to mock the content folder in pre-boot
refs https://github.com/TryGhost/Toolbox/issues/135
- These fixture had to be updated due to hardcoded ids introduced in the fixtures.json for test
- This allows to boot up a Ghost instance without recalculating urls for most common database states
refs https://github.com/TryGhost/Toolbox/issues/135
- These fixtures needs id's to be able to form a stable urls and resources cache that would be used across all e2e tests when booting without a frontend
refs https://github.com/TryGhost/Toolbox/issues/135
- This way the test is performed on a "full" set of posts that will become unified within all e2e tests for performance optimization
closes: https://github.com/TryGhost/Ghost/issues/13739
- Ghost cannot write to the core folder in correctly configured production installations
- Built assets therefore need to be written to the content directory
- Ghost does not overwrite anything in the content folder as part of an upgrade, therefore static files that are provided by Ghost
must still live inside /core
- So as a result, we now have core/frontend/public and content/public
- These two things are meant to improve performance at the cost of reliability.
- Perfect for testing, however I think they make a minimal impact on modern SSDs :(
- Still worth a shot to see if it helps with CI
refs: https://github.com/TryGhost/Toolbox/issues/130
The API version stays at v2 unless we stub the getFrontendApiVersion method. But stubbing the method doesn't get picked up unless we actually restart Ghost.
TODO: Maybe change the default here so we don't need to restart Ghost just to test the current version's API
- These are simple functions that get data from config in a specific format
- They are also used by the topmost part of the application
- Config helpers seems like a reasonable fit to get them out of the web folder
- Functions have also been renamed to try to get them to make more sense
refs https://github.com/TryGhost/Team/issues/1217
- moved top-level `tenorApiKey` to `tenor:apiKey` and added `tenor:contentFilter`
- added base config to `defaults.json`
- updated `public-config.js` and API output serializer to use the new top-level `tenor` key
https://github.com/TryGhost/Toolbox/issues/130
The transaction no longer commits in the promise chain, which wasn't
valid logic for a transaction, since it is commited automatically when
the promise chain resolves, and rollsback automatically when the
promise chain rejects.
This makes code which fails during the transaction error in the right
place, instead of getting stuck here. (Especially good for writing
tests).
The tests for this code can now live in the integration folder.
- This is a minor bugbare, but it will affect some configuration I'm about to do for c8
- I've been wanting to do it for ages, middleware is plural all on it's own so it's an odd affectation in our codebase
- This also only exists in 2 places, everywhere else we use "middleware"
- Sadly it did result in a lot of churn as I did a full find and replace, but consistency is king!
refs https://github.com/TryGhost/Toolbox/issues/116
- Allows to have deterministic routing on each start of the instance - can use cache when starting tests
- To make use of the new fixtures need to place them in the content/data
folder when the test suite is initialized, then the cache detection will
kick in and boot up the UrlService in a constant time intead of having
to check "urlServiceUtils.isFinished"
- this keeps production and test fixtures separate, so that changing the prod fixtures doesn't change the shape of our tests.
- we may still want to test that the production fixtures do what we expect, but that can be handled in a separate integration test, by specifically setting the fixture path
refs: https://github.com/TryGhost/Toolbox/issues/133
- instead of just a collection of utils, we now have a class that manages fixtures
- this should allow us to change the path to fixtures, e.g. between prod/dev and test, so that different fixtures can be loaded by default
- also makes it easier to test the fixture manager code itself
refs 042618fe93
- There's no longer "getValue" method passed around in UrlService, so the were unnecessary and did a wrong thing here :) Yey, less code!
refs https://github.com/TryGhost/Toolbox/issues/127
- Passing around whole instance of a frontend router was an overkill when there are only 3 static pieces of information that needed to be loaded. Extracting the router out makes the UrlGenerator way more readable, tests slimer, and the memory footpring of the process should be slightly lighter
- The toString overloading didn't make sense at the time of this refactor, maybe if there's a concrete usecase we could resurect it in a form of passing in a router's name or something.
refs https://github.com/TryGhost/Toolbox/issues/127
- This is an effor t to define a precise set of data needed for the UrlGenerator to function, which should help with decoupling it from the frontend routes
- This is almost the last piece to free us up from the massive "router" object that has been passed around
refs https://github.com/TryGhost/Toolbox/issues/127
- This is an effor t to define a precise set of data needed for the UrlGenerator to function, which should help with decoupling it from the frontend routes
refs https://github.com/TryGhost/Toolbox/issues/127
- This is an effor t to define a precise set of data needed for the UrlGenerator to function, which should help with decoupling it from the frontend routes
refs https://github.com/TryGhost/Team/issues/1217
- add `tenorApiKey` to `publicConfig.config()
- update canary config endpoint output serializer to include `tenorApiKey` when the `gifsCard` labs flag is enabled
refs https://github.com/TryGhost/Team/issues/1211
In order to override the default OEmbeds for OpenSea NFT's we need a way
to provide out own OEmbed data. We will want this in future too for
custom Twitter embeds, so this has been built in a way which allows
extension.
refs https://github.com/TryGhost/Team/issues/1212
This now emits the event when the service is reconfigured, rather than
when we issue the reconfigure command, which causes the event and the
action to be run in the wrong order. This would then cause knock on effects
of having the database in an undefined state - with stripe data in not linked
to the current Stripe account.
refs https://github.com/TryGhost/Toolbox/issues/120
- Changed endpoint name as it suits way better to the `/upload` postfix convention we use along with all other endpoints that support file uploads
closes https://github.com/TryGhost/Toolbox/issues/120
- Allows to update and upload brand new thumbnail images for previusly uploaded media resources
- The endpoint is available udner alpa flag as part of Admin API at `PUT /media/thumbnail/`
- As an input accepts following parameters:
- *required* `file` field containing an image file
- *required* `url` field containing parent media file URL
- *optional* `ref` as a field to put in an ID to reference the resource on the client side
- The response has following format:
```
{
media: [{
url: 'http://127.0.0.1:2369/content/images/1991/11/nicevideo_thumb.png'
ref: 'unique-id-420'
}]
}
```
refs https://github.com/TryGhost/Toolbox/issues/120
- When editing an uploaded media thumbnail file there'a need to remove existing thumbnail to keep media files:thumbnails 1:1. - Because the API client only has a public URL under which the resource is served it can only provide that as an API input, the `urlToPath` was also added to the base class of LocalStorageAdapter (it might be moved up to the BaseAdapter in the future if we see a need)
refs https://github.com/TryGhost/Toolbox/issues/114
- Files API is meant to be used for non-executable file uploads of all sorts
- The files are stored and retrieved for download as-is
no issue
- Not sure what the purpose of this test is. Seems like it's kind of like an db integrity check where it makes sure the change is double checked before commiting
- This comment removes the block on themes controlling card assets via config
- It also changes the default behaviour from "false" config (doing nothing) to excluding bookmark and gallery card assets
- This is essentially the same thing, as only bookmark and gallery card assets exist at the moment, but it's being done because it makes this feature future-proof for all theme developers.
- As we add new cards, all themes will automatically get the assets to make them work
- As theme developers want to, they can create their own custom assets and disble assets for any cards they support by adding them to the exclude list
- They can also remove any custom code they currently have to support bookmark and gallery cards, and set card_assets: true in package.json to use the defaults instead
- If we register the serve public file middleware for a file that doesn't exist, this will currently throw an ENOENT error
- Instead, we want to fall back to a standard 404 so that this behaves normally
- This will be useful for the card asset service, where the cards.min.css and cards.min.js files may or may not exist
refs https://github.com/TryGhost/Toolbox/issues/95
- The uploaded media thumbnail name should have a "_thumb" postfix to be able to distinguish thumbnails from other files. This can be handy if we decide to store them in a different location in the future.
refs https://github.com/TryGhost/Team/issues/807
The launch wizard completed flag was previously stored at per user level in accessibility column of user table, so an administrator still got the option to complete the launch wizard even if the owner had completed it previously, which is not expected pattern. This change moves the launch complete flag for Admin to common settings from per user level so a site only needs to complete the launch wizard once irrespective of which user completes it
- adds new `editor_is_launch_complete` setting to track if a site launch steps are completed in Admin
- adds new migration util to easily allow adding new setting
- adds migration to introduce new `editor_is_launch_complete` setting
- adds migration to update launch complete flag for a site if any of the users have already completed the launch steps
- Requires the new @tryghost/minifier package
- Adds a new service that will handle taking config from the theme and optionally including assets for Koenig editor cards
- It supports both css and js as cards may need one or both
- For any given config, the tool can find the matching files to include and concat and minify them into one file per type
- Currently has an override in place so that this is not yet customisable in the theme - will remove this override when we're ready for the feature
closes https://github.com/TryGhost/Toolbox/issues/111
- Just like with invalid JSON redirects files we should return a BadRequestError instead of throwing a generic 500 when the redirects.yaml file fails parsing
refs https://github.com/TryGhost/Toolbox/issues/95
- Each media file quires a thumbnail and these changes provide a capability to upload them along with media files.
- The thumbnail file is always required and has to be the format of already supported image formats
- The thumbnail should be uploaded as a part of "thumbnail" attachment in the request
- The regression tests added with this changeset will be claened up and moved to unit-tests (this is a dirty-but-working version!)
- The thumbnail always gets a name of the uploaded media file and keeps it's own extension.
- The thumbnails is accessible under the url present in the "thumbnail_url" reponse field
refs https://github.com/TryGhost/Team/issues/1190
- The assets were broken in Admin when the frontend and admin urls were different
- Fixed the issue by changing the `asset` helper to output absolute URLs when the frontend/admin urls are differents
refs 91efa4605c
- When the instance is booted without any redirects files configured it's not supposed to error but rather default to an "empty" [] redirects configuration.
- Ideally the logic shoudl not contain try/catch block at all and fail as soon as there's any error during the initialization. This wasn't changed at this time due to possible break of existing Ghost instances
refs refs https://linear.app/tryghost/issue/CORE-84/have-a-look-at-the-eggs-redirects-refactor-branch
- The tests needed to have a clean state with empty redirects file, which was previously ensured through "configUtils". Because configUtils don't play ball with the class initialization pattern this approach was chosen
- It's an end-to-end test with lots of logic and pobably would be enough to run against single API endpoint. Leaving it as is and to be improved in the future
refs https://linear.app/tryghost/issue/CORE-84/have-a-look-at-the-eggs-redirects-refactor-branch
- The problem this change is addressing is inability to override config values once the code is extracted into a class+DI pattern
- The work around is restarting the instance with the configuration testing expected behavior - in this case missing or existing types of redirects files
refs https://github.com/TryGhost/Members/commit/9e59f5a9
Since we have a DynamicRedirectManager for handling adding/removing
redirects at runtime, we no longer need the custom-redirects middleware.
The redirects service does however need an init method now to add the
custom redirects at Ghost boot, so it's been refactored into our Class &
DI pattern.
refs https://linear.app/tryghost/issue/CORE-121/create-a-video-storage-adapter
- This is an experimental implementation of video file upload support (audio is yet to follow)
- The storage adapter still needs more thinking as it's almost the same as the "LocalStorgeAdapter" that stores images.
- Also the output serializer skipped use of url utils in favor of inline implementatoin - this should almost certainly be it's own package
refs https://github.com/TryGhost/Team/issues/1178
The "up" migration that this util generates correctly throws if the
pre-requisite data cannot be found in the database. The "down" migration
however was incorrectly mirroring this behaviour of throwing - which
meant that it wasn't idempotent, as it does not require a permission or
role to existing if it wants to move relations between them.
no issue
The way GA flags were introduced means that they stop existing in the `'labs'` setting in the db and are instead forced to always return `true` when checking the flag in the labs service. However, Admin which uses the flags fetches them via the `/settings/` API endpoint which was only returning the raw labs setting db value meaning GA flags appeared to be disabled unless the flag had previously been enabled and no settings save had occured.
- updated the settings bread service to replace the labs setting value with the JSON stringified output of `labs.getAll()` which is the ultimate source-of-truth for a feature being enabled/disabled
- extracted `browse()` behaviour to an internal `_formatBrowse()` method so we can apply the same filtering/modification for output of `browse()` and `edit()`
Co-authored-by: Fabien O'Carroll <fabien@allou.is>
refs https://github.com/TryGhost/Team/issues/1115
This allows users to create Offers for Tier/Cadence pairs in order to
provide discounted subscriptions to Members! We have support for
percentage based discounts & fixed price discounts, either for the first
payment, all payments, or a number of monthly payments.
Offers also have a code, which can be used as an easy way to share them,
as visiting https://site.com/offer-code will automatically open Portal
with the Offer prepopulated.
closes https://github.com/TryGhost/Team/issues/1164
Themes can now define custom settings via their `package.json` file, and use them in templates via `@custom.{setting}`. Values for custom settings can be changed by site owners through a redesigned "Design settings" area in the admin interface.
Full announcement, documentation, and examples will be made available soon.
Co-authored-by:
- Sanne de Vries (@sanne-san)
- Thibaut Patel (@tpatel)