Compare commits

..

74 Commits

Author SHA1 Message Date
Maikel Linke
74fd019863 Update all locales with the latest Transifex translations 2026-03-20 12:23:59 +11:00
Maikel
1207bb5f8f Merge pull request #14023 from mkllnk/api-v0-redirect
Remove compatibility redirect for APIv0
2026-03-20 12:21:26 +11:00
Maikel
d25eea660e Merge pull request #14054 from mkllnk/devise
Bump devise from 4.9.4 to 5.0.3
2026-03-20 12:18:48 +11:00
Maikel
fdfb155682 Merge pull request #14077 from openfoodfoundation/dependabot/npm_and_yarn/flatted-3.4.2
Bump flatted from 3.3.3 to 3.4.2
2026-03-20 12:17:30 +11:00
Maikel Linke
66d6627c89 Spec recently changed code path 2026-03-20 11:51:50 +11:00
Maikel Linke
c5d38d684b Remove repeated navigation to speed up spec 2026-03-20 11:51:50 +11:00
Maikel Linke
c2907b839a Remove ineffective sorting spec
The spec was not really testing the order of users appearing on the
page. It's also a UX detail only visible to super admins which is not
important to test. So I'm not investing time to fix it.
2026-03-20 11:51:50 +11:00
Maikel Linke
ee653bb825 Remove redundant spec description
Admin users are the only one who can manage users.
2026-03-20 11:51:50 +11:00
Maikel Linke
75616e69e7 Bump devise from 4.9.4 to 5.0.3 2026-03-20 11:51:47 +11:00
Maikel Linke
b4b3e21cf6 Replace deprecated option bypass
- https://github.com/heartcombo/devise/pull/5803
2026-03-20 11:32:49 +11:00
Maikel Linke
ce90ec0f5b Ignore unused authentication_token column 2026-03-20 11:32:49 +11:00
Maikel Linke
2998432744 Remove use of devise token_authenticable
Our production servers don't show any use of this feature. The
associated column is nil for all users.

The gem has not been updated in seven years and it's blocking an
important upgrade of devise.
2026-03-20 11:32:48 +11:00
Maikel
c5aaecf76a Merge pull request #13961 from mkllnk/taler-checkout-stock-error
Taler checkout stock error
2026-03-20 11:29:58 +11:00
Gaetan Craig-Riou
2e64f54740 Merge pull request #14076 from openfoodfoundation/dependabot/bundler/bcrypt-3.1.22
Bump bcrypt from 3.1.20 to 3.1.22
2026-03-20 10:24:54 +11:00
Gaetan Craig-Riou
c7de67a14f Merge pull request #14074 from openfoodfoundation/dependabot/bundler/json-2.19.2
Bump json from 2.19.1 to 2.19.2
2026-03-20 10:05:19 +11:00
dependabot[bot]
545e69835d Bump flatted from 3.3.3 to 3.4.2
Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.2.
- [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 22:57:24 +00:00
Gaetan Craig-Riou
e3a757bd2d Merge pull request #14070 from openfoodfoundation/dependabot/bundler/loofah-2.25.1
Bump loofah from 2.25.0 to 2.25.1
2026-03-20 09:55:44 +11:00
dependabot[bot]
8223d1ce52 Bump bcrypt from 3.1.20 to 3.1.22
Bumps [bcrypt](https://github.com/bcrypt-ruby/bcrypt-ruby) from 3.1.20 to 3.1.22.
- [Release notes](https://github.com/bcrypt-ruby/bcrypt-ruby/releases)
- [Changelog](https://github.com/bcrypt-ruby/bcrypt-ruby/blob/master/CHANGELOG)
- [Commits](https://github.com/bcrypt-ruby/bcrypt-ruby/compare/v3.1.20...v3.1.22)

---
updated-dependencies:
- dependency-name: bcrypt
  dependency-version: 3.1.22
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 18:48:18 +00:00
dependabot[bot]
167846138f Bump json from 2.19.1 to 2.19.2
Bumps [json](https://github.com/ruby/json) from 2.19.1 to 2.19.2.
- [Release notes](https://github.com/ruby/json/releases)
- [Changelog](https://github.com/ruby/json/blob/master/CHANGES.md)
- [Commits](https://github.com/ruby/json/compare/v2.19.1...v2.19.2)

---
updated-dependencies:
- dependency-name: json
  dependency-version: 2.19.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 13:02:05 +00:00
Maikel
243e70427d Merge pull request #14065 from openfoodfoundation/dependabot/bundler/debug-1.11.1
Bump debug from 1.11.0 to 1.11.1
2026-03-19 13:54:54 +11:00
Maikel
0ae3c8d668 Merge pull request #14063 from openfoodfoundation/dependabot/npm_and_yarn/mini-css-extract-plugin-2.10.1
Bump mini-css-extract-plugin from 2.10.0 to 2.10.1
2026-03-19 13:54:17 +11:00
Maikel
c8f46e52d3 Merge pull request #14062 from openfoodfoundation/dependabot/npm_and_yarn/sass-embedded-1.98.0
Bump sass-embedded from 1.97.3 to 1.98.0
2026-03-19 13:53:40 +11:00
dependabot[bot]
131a916d99 Bump loofah from 2.25.0 to 2.25.1
Bumps [loofah](https://github.com/flavorjones/loofah) from 2.25.0 to 2.25.1.
- [Release notes](https://github.com/flavorjones/loofah/releases)
- [Changelog](https://github.com/flavorjones/loofah/blob/main/CHANGELOG.md)
- [Commits](https://github.com/flavorjones/loofah/compare/v2.25.0...v2.25.1)

---
updated-dependencies:
- dependency-name: loofah
  dependency-version: 2.25.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 21:07:53 +00:00
dependabot[bot]
9dd9e08694 Bump debug from 1.11.0 to 1.11.1
Bumps [debug](https://github.com/ruby/debug) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/ruby/debug/releases)
- [Commits](https://github.com/ruby/debug/compare/v1.11.0...v1.11.1)

---
updated-dependencies:
- dependency-name: debug
  dependency-version: 1.11.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 09:34:28 +00:00
dependabot[bot]
e7180e956f Bump mini-css-extract-plugin from 2.10.0 to 2.10.1
Bumps [mini-css-extract-plugin](https://github.com/webpack/mini-css-extract-plugin) from 2.10.0 to 2.10.1.
- [Release notes](https://github.com/webpack/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack/mini-css-extract-plugin/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/mini-css-extract-plugin/compare/v2.10.0...v2.10.1)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  dependency-version: 2.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 09:33:00 +00:00
dependabot[bot]
7f312caa25 Bump sass-embedded from 1.97.3 to 1.98.0
Bumps [sass-embedded](https://github.com/sass/embedded-host-node) from 1.97.3 to 1.98.0.
- [Changelog](https://github.com/sass/embedded-host-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/embedded-host-node/compare/1.97.3...1.98.0)

---
updated-dependencies:
- dependency-name: sass-embedded
  dependency-version: 1.98.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 09:32:55 +00:00
Maikel Linke
8f0d4d23a7 Restore Stripe spec stubs as todo list 2026-03-18 14:54:44 +11:00
Maikel Linke
d56b4b4109 Make locale spec more accurate 2026-03-18 14:49:35 +11:00
Maikel Linke
7a01409f5c Save time by avoiding page visit 2026-03-18 14:49:34 +11:00
Maikel Linke
5c634c269b Remove unused helpers from spec 2026-03-18 14:49:34 +11:00
Maikel Linke
bf22484add Add default locale to fallbacks
The comment about falling back to the default locale came from the first
commit, when the config was just `fallbacks = true`. The fallback logic
is a lot more sophisticated now and we can supply the country's default
locale as first fallback and our source locale `en` as last resort. It
should contain everything.

In the future, we may want to support maps like Canadian French can fall
back to original French. I18n supports this but providing the config per
isntance may be a bit tricky.
2026-03-18 14:49:34 +11:00
Maikel Linke
1696dd2de6 DRY language fallback config 2026-03-18 14:49:34 +11:00
Maikel Linke
63988fff4f Configure test locales like other envs
The locale config is set in application.rb from environment variables
already. We don't need to repeat that logic in test.rb. And because it
was outdated, the language switcher was actually broken in the test
environment. We did have an English selector for the fallback `en` even
though we were already displaying English as en_TST. And after
switchting to Spanish, we could switch back because en_TST was not in
the available locales.

I now fixed the test with the right assumption and the config to solve
the problem.
2026-03-18 14:49:15 +11:00
Maikel Linke
d56790b71e Remove unneeded context blocks
Best viewed ignoring whitespace.
2026-03-18 13:23:54 +11:00
Maikel Linke
c2f725b20c Remove redundant language setup
We tested earlier already that we have the needed languages available.
2026-03-18 13:21:27 +11:00
Maikel Linke
b939d41bf5 Remove useless negative spec
Any change in markup would invalidate the test.

It is unlikely to ever fail and then it would not be a big problem.
2026-03-18 13:18:15 +11:00
Maikel Linke
91c4ba03cd Remove more expensive specs 2026-03-18 13:15:35 +11:00
Maikel Linke
3f29cdab3c Combine specs and add detail 2026-03-18 13:14:57 +11:00
Maikel Linke
60a4d36408 Remove expensive without really new coverage 2026-03-18 13:08:24 +11:00
Maikel Linke
9513c07c2f Remove ineffective test
It would pass even if locales were broken.
2026-03-18 13:05:23 +11:00
Maikel Linke
4b5fd2495f Restore multilingual spec 2026-03-18 13:03:48 +11:00
David Cook
939ae20081 Merge pull request #14053 from chahmedejaz/fix/wkhtmltopdf-binary-install-issue
Fix wkhtmltopdf-binary installation error
2026-03-18 12:44:10 +11:00
David Cook
78e9524767 Add comment 2026-03-18 12:41:28 +11:00
Maikel
3f5b7aaea9 Merge pull request #14051 from openfoodfoundation/dependabot/bundler/mini_portile2-2.8.9
Bump mini_portile2 from 2.8.6 to 2.8.9
2026-03-18 12:20:40 +11:00
Maikel
00ab4379c9 Merge pull request #14050 from openfoodfoundation/dependabot/bundler/aws-sdk-s3-1.215.0
Bump aws-sdk-s3 from 1.213.0 to 1.215.0
2026-03-18 12:19:39 +11:00
Maikel
9dec68032c Merge pull request #14049 from openfoodfoundation/dependabot/npm_and_yarn/jest-30.3.0
Bump jest from 30.2.0 to 30.3.0
2026-03-18 12:18:52 +11:00
Maikel
e449ae95d5 Merge pull request #14052 from dacook/linter-ubuntu-version
Use same ubuntu version for linters to share bundler cache
2026-03-18 12:17:27 +11:00
Ahmed Ejaz
d0af6ddcc1 Fix wkhtmltopdf-binary installation by specifying the original source in Gemfile 2026-03-18 06:07:59 +05:00
dependabot[bot]
a17d3f34d9 Bump jest from 30.2.0 to 30.3.0
Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) from 30.2.0 to 30.3.0.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.3.0/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 00:49:46 +00:00
David Cook
6c5d49ae33 Merge pull request #14048 from openfoodfoundation/dependabot/npm_and_yarn/jest-environment-jsdom-30.3.0
Bump jest-environment-jsdom from 30.2.0 to 30.3.0
2026-03-18 11:48:31 +11:00
David Cook
7d4389de4a Use same ubuntu version for linters to share bundler cache 2026-03-18 11:26:56 +11:00
David Cook
93c9181c3f Merge pull request #14021 from mkllnk/money
Remove dead code from Spree::Money
2026-03-18 10:50:54 +11:00
David Cook
7473a2f0bd Merge pull request #14020 from mkllnk/flaky-invite-spec
Expect UI change before emails
2026-03-18 10:49:03 +11:00
dependabot[bot]
af8544a4fa Bump mini_portile2 from 2.8.6 to 2.8.9
Bumps [mini_portile2](https://github.com/flavorjones/mini_portile) from 2.8.6 to 2.8.9.
- [Release notes](https://github.com/flavorjones/mini_portile/releases)
- [Changelog](https://github.com/flavorjones/mini_portile/blob/main/CHANGELOG.md)
- [Commits](https://github.com/flavorjones/mini_portile/compare/v2.8.6...v2.8.9)

---
updated-dependencies:
- dependency-name: mini_portile2
  dependency-version: 2.8.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-17 09:39:55 +00:00
dependabot[bot]
be156e5621 Bump aws-sdk-s3 from 1.213.0 to 1.215.0
Bumps [aws-sdk-s3](https://github.com/aws/aws-sdk-ruby) from 1.213.0 to 1.215.0.
- [Release notes](https://github.com/aws/aws-sdk-ruby/releases)
- [Changelog](https://github.com/aws/aws-sdk-ruby/blob/version-3/gems/aws-sdk-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-ruby/commits)

---
updated-dependencies:
- dependency-name: aws-sdk-s3
  dependency-version: 1.215.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-17 09:34:41 +00:00
dependabot[bot]
ec8fcecbd6 Bump jest-environment-jsdom from 30.2.0 to 30.3.0
Bumps [jest-environment-jsdom](https://github.com/jestjs/jest/tree/HEAD/packages/jest-environment-jsdom) from 30.2.0 to 30.3.0.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.3.0/packages/jest-environment-jsdom)

---
updated-dependencies:
- dependency-name: jest-environment-jsdom
  dependency-version: 30.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-17 09:32:53 +00:00
Maikel
35ae3a424a Merge pull request #14046 from openfoodfoundation/RachL-patch-1
Update release task template
2026-03-17 10:34:58 +11:00
Maikel
c2c83898a2 Merge pull request #14045 from openfoodfoundation/dependabot/bundler/knapsack_pro-9.2.3
Bump knapsack_pro from 9.2.2 to 9.2.3
2026-03-17 10:33:27 +11:00
Maikel
2879f77aa1 Merge pull request #14044 from openfoodfoundation/dependabot/bundler/json-2.19.1
Bump json from 2.18.1 to 2.19.1
2026-03-17 10:32:57 +11:00
Rachel Arnould
68032657c3 Update release task template
Add a reminder + change the last step
2026-03-16 12:10:17 +01:00
dependabot[bot]
be4037d05a Bump knapsack_pro from 9.2.2 to 9.2.3
Bumps [knapsack_pro](https://github.com/KnapsackPro/knapsack_pro-ruby) from 9.2.2 to 9.2.3.
- [Changelog](https://github.com/KnapsackPro/knapsack_pro-ruby/blob/main/CHANGELOG.md)
- [Commits](https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v9.2.2...v9.2.3)

---
updated-dependencies:
- dependency-name: knapsack_pro
  dependency-version: 9.2.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 10:05:34 +00:00
dependabot[bot]
9661e8a53e Bump json from 2.18.1 to 2.19.1
Bumps [json](https://github.com/ruby/json) from 2.18.1 to 2.19.1.
- [Release notes](https://github.com/ruby/json/releases)
- [Changelog](https://github.com/ruby/json/blob/master/CHANGES.md)
- [Commits](https://github.com/ruby/json/compare/v2.18.1...v2.19.1)

---
updated-dependencies:
- dependency-name: json
  dependency-version: 2.19.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 10:03:56 +00:00
David Cook
5e93c27277 Merge pull request #14041 from openfoodfoundation/dependabot/bundler/puma-7.2.0
Bump puma from 6.5.0 to 7.2.0
2026-03-16 09:57:17 +11:00
dependabot[bot]
fcb3b67efb Bump puma from 6.5.0 to 7.2.0
Bumps [puma](https://github.com/puma/puma) from 6.5.0 to 7.2.0.
- [Release notes](https://github.com/puma/puma/releases)
- [Changelog](https://github.com/puma/puma/blob/main/History.md)
- [Commits](https://github.com/puma/puma/compare/v6.5.0...v7.2.0)

---
updated-dependencies:
- dependency-name: puma
  dependency-version: 7.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-13 09:33:46 +00:00
Maikel Linke
0629153362 Complete code coverage on Taler error handling 2026-03-11 16:05:54 +11:00
Maikel Linke
5e871fc71e Expect UI change before emails
Fixing a flaky spec.
2026-03-11 15:30:39 +11:00
Maikel Linke
35e03ea3c7 Check stock before completing order after Taler payment 2026-03-11 15:12:21 +11:00
Maikel Linke
ee7ed56f44 Remove unused method Spree::Money#format 2026-03-11 14:56:28 +11:00
Maikel Linke
4140209820 Remove unused delegator to Money#cents 2026-03-11 14:56:28 +11:00
Maikel Linke
1bb4fdf294 Remove compatibility redirect for APIv0 2026-03-11 14:52:11 +11:00
Maikel Linke
9ca1a9e33f Allow orders to be paid before checkout
Taler puts the payment completion into the hands of the user. So we
can't strictly finalise the payment and order together.

And in the bigger picture, it should be okay if a payment goes through
but we have to abort checkout due to stock issues. Then we want to be
able to check out again, using the existing complete payment. Any
refunds can be handled later by the shop owner.
2026-03-11 14:46:06 +11:00
Maikel Linke
62af416696 Avoid useless page visit causing spec flakiness 2026-03-11 14:41:40 +11:00
Maikel Linke
bcf39acebc Remove confusing if-branch in shared examples 2026-03-11 14:41:40 +11:00
Maikel Linke
82186118a7 Remove long inactive specs
Several years ago, some checkout features got rewritten and some specs
became invalid. They had been set to pending to keep the option of
rewriting them one day. Some were re-written. But I'm deleting the
remainder.

If we haven't "needed" these specs for several years then I question
their use. System specs are expensive and should only cover the most
common scenarios or the ones we know could go wrong (after a bug
report). We can always write new specs if needed. Otherwise they are
just adding to maintenance cost.
2026-03-11 14:41:40 +11:00
37 changed files with 940 additions and 1125 deletions

View File

@@ -13,6 +13,9 @@ CAPYBARA_MAX_WAIT_TIME="10"
# successful fallback to `en`.
LOCALE="en_TST"
# For multilingual - ENV doesn't have array so pass it as string with commas
AVAILABLE_LOCALES="en_TST,es,pt"
OFN_REDIS_JOBS_URL="redis://localhost:6379/2"
SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@@ -32,7 +32,8 @@ assignees: ''
- [ ] Stripe with Authentication required card: `4000002760003184` as shopper and as Admin. As admin, check authorization through customer account `/account#/transactions` and email.
- [ ] Pay with Paypal.
- [ ] Order on mobile.
- [ ] Notify a deployer to deploy it.
- [ ] Explore a bit the platform to see if there is nothing unusual (15 min. max)
- [ ] Comment on the issue: if it's all good, move to ready to go. If there are issues, don't forget to mention your Desktop/Browser version and number and ping @ reviewers so someone available can have a look
## 3. Deployment at beginning of week

View File

@@ -5,7 +5,7 @@ permissions:
jobs:
lint:
name: reviewdog
runs-on: ubuntu-latest
runs-on: ubuntu-22.04 # same as build.yml so it can share bundler cache
steps:
- uses: actions/checkout@v3

View File

@@ -63,7 +63,6 @@ gem "taler"
gem 'devise'
gem 'devise-encryptable'
gem 'devise-i18n'
gem 'devise-token_authenticatable'
gem 'jwt', '~> 2.3'
gem 'oauth2', '~> 1.4.7' # Used for Stripe Connect
@@ -113,7 +112,7 @@ gem "turbo-rails"
gem 'combine_pdf'
gem 'wicked_pdf', github: "openfoodfoundation/wicked_pdf", branch: "master"
gem 'wkhtmltopdf-binary'
gem 'wkhtmltopdf-binary', source: 'https://rubygems.org' # due to https://github.com/gem-coop/gem.coop/issues/36
gem 'immigrant'
gem 'roo' # read spreadsheets

View File

@@ -185,7 +185,7 @@ GEM
ast (2.4.3)
attr_required (1.0.2)
aws-eventstream (1.4.0)
aws-partitions (1.1223.0)
aws-partitions (1.1227.0)
aws-sdk-core (3.243.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
@@ -197,15 +197,15 @@ GEM
aws-sdk-kms (1.122.0)
aws-sdk-core (~> 3, >= 3.241.4)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.213.0)
aws-sdk-core (~> 3, >= 3.241.4)
aws-sdk-s3 (1.215.0)
aws-sdk-core (~> 3, >= 3.243.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
base64 (0.3.0)
bcp47_spec (0.2.1)
bcrypt (3.1.20)
bcrypt (3.1.22)
benchmark (0.5.0)
bigdecimal (3.3.1)
bindata (2.5.1)
@@ -279,13 +279,13 @@ GEM
datafoodconsortium-connector (1.2.0)
virtual_assembly-semantizer (~> 1.0, >= 1.0.5)
date (3.5.1)
debug (1.11.0)
debug (1.11.1)
irb (~> 1.10)
reline (>= 0.3.8)
devise (4.9.4)
devise (5.0.3)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
railties (>= 7.0)
responders
warden (~> 1.2.3)
devise-encryptable (0.2.0)
@@ -293,8 +293,6 @@ GEM
devise-i18n (1.15.0)
devise (>= 4.9.0)
rails-i18n
devise-token_authenticatable (1.1.0)
devise (>= 4.0.0, < 5.0.0)
diff-lcs (1.6.2)
digest (3.2.1)
docile (1.4.1)
@@ -443,7 +441,7 @@ GEM
thor (>= 0.14, < 2.0)
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (2.18.1)
json (2.19.2)
json-canonicalization (1.0.0)
json-jwt (1.17.0)
activesupport (>= 4.2)
@@ -469,7 +467,8 @@ GEM
activesupport (>= 4.2)
jwt (2.10.2)
base64
knapsack_pro (9.2.2)
knapsack_pro (9.2.3)
logger
rake
language_server-protocol (3.17.0.5)
launchy (3.0.0)
@@ -484,7 +483,7 @@ GEM
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.7.0)
loofah (2.25.0)
loofah (2.25.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.9.0)
@@ -506,7 +505,7 @@ GEM
mini_magick (5.3.1)
logger
mini_mime (1.1.5)
mini_portile2 (2.8.6)
mini_portile2 (2.8.9)
minitest (6.0.2)
drb (~> 2.0)
prism (~> 1.5)
@@ -531,7 +530,7 @@ GEM
net-protocol
newrelic_rpm (9.24.0)
nio4r (2.7.5)
nokogiri (1.19.1)
nokogiri (1.19.2)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nokogiri-html5-inference (0.3.0)
@@ -613,7 +612,7 @@ GEM
eventmachine_httpserver
http_parser.rb (~> 0.8.0)
multi_json
puma (6.5.0)
puma (7.2.0)
nio4r (~> 2.0)
query_count (1.1.1)
activerecord (>= 4.2)
@@ -969,12 +968,16 @@ GEM
websocket-extensions (0.1.5)
whenever (1.1.0)
chronic (>= 0.6.3)
wkhtmltopdf-binary (0.12.6.10)
xml-simple (1.1.8)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.7.5)
GEM
remote: https://rubygems.org/
specs:
wkhtmltopdf-binary (0.12.6.10)
PLATFORMS
ruby
@@ -1013,7 +1016,6 @@ DEPENDENCIES
devise
devise-encryptable
devise-i18n
devise-token_authenticatable
dfc_provider!
digest
dotenv
@@ -1125,7 +1127,7 @@ DEPENDENCIES
webmock
whenever
wicked_pdf!
wkhtmltopdf-binary
wkhtmltopdf-binary!
RUBY VERSION
ruby 3.4.8p72

View File

@@ -2,15 +2,36 @@
module PaymentGateways
class TalerController < BaseController
include OrderStockCheck
include OrderCompletion
class StockError < StandardError
end
# The Taler merchant backend has taken the payment.
# Now we just need to confirm that and update our local database
# before finalising the order.
def confirm
payment = Spree::Payment.find(params[:payment_id])
# Process payment early because it's probably paid already.
# We want to capture that before any validations raise errors.
unless payment.process!
return redirect_to order_failed_route(step: "payment")
end
@order = payment.order
process_payment_completion!
OrderLocker.lock_order_and_variants(@order) do
raise StockError unless sufficient_stock?
process_payment_completion!
end
rescue Spree::Core::GatewayError => e
flash[:notice] = e.message
redirect_to order_failed_route(step: "payment")
rescue StockError
flash[:notice] = t("checkout.payment_cancelled_due_to_stock")
redirect_to main_app.checkout_step_path(step: "details")
end
end
end

View File

@@ -102,7 +102,7 @@ module Spree
def sign_in_if_change_own_password
return unless spree_current_user == @user && @user.password.present?
sign_in(@user, event: :authentication, bypass: true)
bypass_sign_in(@user)
end
def new_email_unconfirmed?

View File

@@ -674,8 +674,6 @@ module Spree
end
def process_each_payment
raise Core::GatewayError, Spree.t(:no_pending_payments) if pending_payments.empty?
pending_payments.each do |payment|
if payment.amount.zero? && zero_priced_order?
payment.update_columns(state: "completed", captured_at: Time.zone.now)

View File

@@ -5,10 +5,11 @@ module Spree
include SetUnusedAddressFields
self.belongs_to_required_by_default = false
self.ignored_columns += [:authentication_token]
searchable_attributes :email
devise :database_authenticatable, :token_authenticatable, :registerable, :recoverable,
devise :database_authenticatable, :registerable, :recoverable,
:rememberable, :trackable, :validatable, :omniauthable,
:encryptable, :confirmable,
encryptor: 'authlogic_sha512', reconfirmable: true,

View File

@@ -158,10 +158,10 @@ module Openfoodnetwork
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# The default locale is set in the environment.
config.i18n.default_locale = OpenFoodNetwork::I18nConfig.default_locale
config.i18n.available_locales = OpenFoodNetwork::I18nConfig.available_locales
config.i18n.fallbacks = OpenFoodNetwork::I18nConfig.fallbacks
I18n.locale = config.i18n.locale = config.i18n.default_locale
# Calculate digests for locale files so we can know when they change

View File

@@ -111,8 +111,6 @@ Rails.application.configure do
# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true
config.i18n.fallbacks = [:en]
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true

View File

@@ -91,10 +91,6 @@ Rails.application.configure do
# Use https in email links
config.action_mailer.default_url_options = { protocol: 'https' }
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = [:en]
# Send deprecation notices to registered listeners
config.active_support.deprecation = :notify

View File

@@ -60,10 +60,6 @@ Openfoodnetwork::Application.configure do
# Enable threaded mode
# config.threadsafe!
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
config.i18n.fallbacks = [:en]
# Send deprecation notices to registered listeners
config.active_support.deprecation = :notify
end

View File

@@ -89,12 +89,6 @@ Rails.application.configure do
# Raises error for missing translations.
config.i18n.raise_on_missing_translations = true
# Tests assume English text on the site.
config.i18n.default_locale = "en"
config.i18n.available_locales = ['en', 'es', 'pt']
config.i18n.fallbacks = [:en]
I18n.locale = config.i18n.locale = config.i18n.default_locale
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true

View File

@@ -138,11 +138,6 @@ Devise.setup do |config|
config.case_insensitive_keys = [:email]
end
Devise::TokenAuthenticatable.setup do |config|
# Defines name of the authentication token params key
config.token_authentication_key = :auth_token
end
if ENV["OPENID_APP_ID"].present? && ENV["OPENID_APP_SECRET"].present?
Devise.setup do |config|
site = if Rails.env.development?

View File

@@ -946,6 +946,12 @@ cy:
clone:
success: Llwyddwyd i glonior cynnyrch
error: Methwyd â chlonior cynnyrch
tag_rules:
rules_per_tag:
one: "%{tag}has 1 rule"
two: "%{tag}has %{count} rules"
many: "%{tag}has %{count} rules"
other: "%{tag} has %{count} rules"
product_import:
title: Mewnforio Cynnyrch
file_not_found: Ni ddaethpwyd o hyd i ffeil neu ni ellid ei hagor

View File

@@ -272,6 +272,10 @@ en_GB:
no_default_card: "^No default card available for this customer"
shipping_method:
not_available_to_shop: "is not available to %{shop}"
user_invitation:
attributes:
email:
is_already_manager: is already a manager
card_details: "Card details"
card_type: "Card type"
card_type_is: "Card type is"
@@ -421,6 +425,11 @@ en_GB:
authorization_required:
subject: "A payment requires authorisation from the customer"
message: "A payment for order %{order_number} requires additional authorisation from the customer. The customer has been notified via email and the payment will appear as pending until it is authorised."
refund_available:
subject: "Refund from %{shop}"
message: |
Your payment of %{amount} to %{shop} is being refunded
Accept your refund following the link below.
producer_mailer:
order_cycle:
subject: "Order cycle report for %{producer}"
@@ -802,6 +811,7 @@ en_GB:
bill_address: "Billing Address"
ship_address: "Shipping Address"
balance: "Balance"
credit: "Available Credit"
update_address_success: "Address updated successfully."
update_address_error: "Sorry! Please input all of the required fields!"
edit_bill_address: "Edit Billing Address"
@@ -816,12 +826,16 @@ en_GB:
guest_label: "Guest checkout"
credit_owed: "Credit Owed"
balance_due: "Balance Due"
id: Id
destroy:
has_associated_subscriptions: "Delete failed: This customer has active subscriptions. Cancel them first."
customer_account_transaction:
index:
available_credit: "Available credit: %{available_credit}"
transaction_date: Transaction Date
description: Description
amount: Amount
created_by: Created by
running_balance: Running balance
column_preferences:
bulk_update:
@@ -1414,10 +1428,12 @@ en_GB:
show_hide_payment: 'Show or Hide payment methods at checkout'
show_hide_order_cycles: 'Show or Hide order cycles in my shopfront'
users:
description: The users with permission to manage this enterprise.
legend: "Users"
email_confirmation_notice_html: "Email confirmation is pending. We've sent a confirmation email to %{email}."
resend: Resend
contact: "Contact"
manager: "Manager"
owner: 'Owner'
contact_tip: "The manager who will receive enterprise emails for orders and notifications. Must have a confirmed email adress."
owner_tip: The primary user responsible for this enterprise.
@@ -1428,6 +1444,8 @@ en_GB:
invite_manager: "Invite Manager"
email_confirmed: "Email confirmed"
email_not_confirmed: "Email not confirmed"
set_as_contact: "Set %{email} as contact"
set_as_owner: "Set %{email} as owner"
vouchers:
legend: Vouchers
voucher_code: Voucher Code
@@ -1776,6 +1794,11 @@ en_GB:
images: "Images"
contact: "Contact"
web: "Web Resources"
stimulus_pagination:
navigation: Pagination
page: "Page %{number}"
previous: Previous page
next: Next page
enterprise_issues:
create_new: Create New
resend_email: Resend Email
@@ -2005,7 +2028,10 @@ en_GB:
user_invitations:
new:
back: Back
description: "Invite a user to sign up and become a manager of this enterprise."
eg_email_address: e.g. email address of a new or existing user
email: Email
invite_new_user: Invite a new user
invite: Invite
vouchers:
new:
@@ -2405,7 +2431,7 @@ en_GB:
system_step3_text: "Set up payment methods and add your collection and delivery options."
cta_headline: "The sustainable food network of our future."
cta_label: "Start shopping"
stats_headline: "Join over 18k shoppers and producers in creating a new food system"
stats_headline: "Join over 21k shoppers and producers in creating a new food system"
stats_producers: "food producers"
stats_shops: "food shops"
stats_shoppers: "food shoppers"
@@ -2439,6 +2465,7 @@ en_GB:
order_total: Total order
order_payment: "Paying via:"
no_payment_required: "No payment required"
credit_used: "Credit used: %{amount}"
customer_credit: Credit
order_billing_address: Billing address
order_delivery_on: Delivery on
@@ -3423,6 +3450,7 @@ en_GB:
no_orders_found: "No Orders Found"
order_information: "Order Information"
new_payment: "New Payment"
credit_customer: Credit customer
create_or_update_invoice: "Create or Update Invoice"
date_completed: "Date Completed"
amount: "Amount"
@@ -4020,6 +4048,7 @@ en_GB:
items_cannot_be_shipped: "Items cannot be shipped"
gateway_config_unavailable: "Gateway config unavailable"
gateway_error: "Payment failed"
internal_payment_not_voidable: Payment not voidable
more: "More"
new_adjustment: "New adjustment"
new_tax_category: "New Tax Category"
@@ -4514,6 +4543,7 @@ en_GB:
paypalexpress: "PayPal Express"
stripesca: "Stripe SCA "
taler: "Taler"
customercredit: "Customer Credit"
payments:
source_forms:
stripe:
@@ -4521,6 +4551,7 @@ en_GB:
submitting_payment: Submitting payment...
paypal:
no_payment_via_admin_backend: Paypal payments cannot be captured in the Backoffice
customer_credit_successful: Customer has been successfully credited!
products:
image_upload_error: "Please upload the image in JPG, PNG, GIF, SVG or WEBP format."
image_not_processable: "Image attachment is not a valid image."
@@ -4836,6 +4867,7 @@ en_GB:
orders: Orders
cards: Credit Cards
transactions: Transactions
customer_account_transactions: Customer Transactions
settings: Account Settings
unconfirmed_email: "Pending email confirmation for: %{unconfirmed_email}. Your email address will be updated once the new email is confirmed."
orders:
@@ -4846,6 +4878,9 @@ en_GB:
authorisation_required: Authorisation Required
authorise: Authorise
customer_account_transactions:
title: Customer Transactions
credit_available: "Credit available: %{credit}"
transaction_date: Transaction Date
description: Description
amount: Amount
running_balance: Running balance
@@ -4988,3 +5023,22 @@ en_GB:
invisible_captcha:
sentence_for_humans: "Please leave empty"
timestamp_error_message: "Please try again after 5 seconds."
api_customer_credit: "API credit: %{description}"
credit_payment_method:
name: Customer credit
description: Allow customer to pay with credit
success: Payment with credit was sucessful
void_success: Credit void was sucessful
order_payment_description: "Customer credit: Payment for order: %{order_number}"
order_void_description: "Customer credit: Refund for order: %{order_number}"
errors:
customer_not_found: Customer not found
missing_payment: Missing payment
credit_payment_method_missing: Credit payment method is missing
no_credit_available: No credit available
not_enough_credit_available: Not enough credit available
orders:
customer_credit_service:
no_credit_owed: No credit owed
credit_payment_method_missing: Customer credit payment method is missing, please check configuration
refund_sucessful: Refund successful!

View File

@@ -549,7 +549,7 @@ hu:
on_hand: "Készlet"
on_demand: "Igény szerint"
tax_category: "Adókategória"
inherits_properties: "Örökölje a tulajdonságokat?"
inherits_properties: "Tulajdonságok öröklése?"
import_date: "Importálás dátuma"
actions: Műveletek
tags: Címkék
@@ -562,7 +562,7 @@ hu:
on_hand: "Készlet"
on_demand: "Igény szerint"
tax_category: "Adókategória"
inherits_properties: "Örökölje a tulajdonságokat?"
inherits_properties: "Tulajdonságok öröklése?"
import_date: "Importálás dátuma"
actions:
edit: Szerkesztés
@@ -798,7 +798,7 @@ hu:
display_as: Megjelenítés mint
category: Kategória
tax_category: Adókategória
inherits_properties?: Örökölje a tulajdonságokat?
inherits_properties?: Tulajdonságok öröklése?
av_on: "Av. On"
import_date: Importált
upload_an_image: Tölts fel egy képet
@@ -1877,7 +1877,9 @@ hu:
user_invitations:
new:
back: Vissza
description: "Hívj meg egy felhasználót, hogy regisztráljon és a vállalkozás menedzsere legyen."
email: Email
invite_new_user: Új felhasználó meghívása
invite: Meghívás
vouchers:
new:
@@ -4371,7 +4373,7 @@ hu:
display_as: Megjelenítés mint
category: Kategória
tax_category: Adókategória
inherits_properties?: Örökölje a tulajdonságokat?
inherits_properties?: Tulajdonságok öröklése?
av_on: "Av. On"
import_date: "Importálás dátuma"
products_variant:

View File

@@ -95,8 +95,5 @@ Openfoodnetwork::Application.routes.draw do
resources :customer_account_transaction, only: [:create]
end
match '*path', to: redirect(path: "/api/v0/%{path}"), via: :all,
constraints: { path: /(?!v[0-9]).+/ }
end
end

View File

@@ -20,6 +20,10 @@ module OpenFoodNetwork
(selectable_locales + [default_locale, source_locale]).uniq
end
def self.fallbacks
[default_locale, source_locale].uniq
end
# The default locale that is used when the user doesn't have a preference.
def self.default_locale
ENV["LOCALE"] || ENV["I18N_LOCALE"] || source_locale

View File

@@ -6,8 +6,6 @@ module Spree
class Money
attr_reader :money
delegate :cents, to: :money
def initialize(amount, options = {})
@money = ::Monetize.parse([amount, options[:currency] || CurrentConfig.get(:currency)].join)
@@ -32,10 +30,6 @@ module Spree
.html_safe # rubocop:disable Rails/OutputSafety
end
def format(options = {})
@money.format(@options.merge!(options))
end
def ==(other)
@money == other.money
end

View File

@@ -15,7 +15,7 @@ RSpec.describe I18nHelper do
it "sets the default locale" do
helper.set_locale
expect(I18n.locale).to eq :en
expect(I18n.locale).to eq :en_TST
end
it "sets the chosen locale" do
@@ -36,11 +36,11 @@ RSpec.describe I18nHelper do
it "ignores unavailable locales" do
allow(helper).to receive(:params) { { locale: "xx" } }
helper.set_locale
expect(I18n.locale).to eq :en
expect(I18n.locale).to eq :en_TST
end
it "remembers the last chosen locale" do
allow(helper).to receive(:params) { { locale: "en" } }
allow(helper).to receive(:params) { { locale: "en_TST" } }
helper.set_locale
allow(helper).to receive(:params) { { locale: "es" } }
@@ -71,7 +71,7 @@ RSpec.describe I18nHelper do
allow(helper).to receive(:params) { {} }
helper.set_locale
expect(I18n.locale).to eq :en
expect(I18n.locale).to eq :en_TST
end
end
@@ -82,7 +82,7 @@ RSpec.describe I18nHelper do
it "sets the default locale" do
helper.set_locale
expect(I18n.locale).to eq :en
expect(I18n.locale).to eq :en_TST
end
it "sets the chosen locale" do
@@ -102,7 +102,7 @@ RSpec.describe I18nHelper do
end
it "remembers the last chosen locale" do
allow(helper).to receive(:params) { { locale: "en" } }
allow(helper).to receive(:params) { { locale: "en_TST" } }
helper.set_locale
allow(helper).to receive(:params) { { locale: "es" } }

View File

@@ -17,7 +17,7 @@ RSpec.describe Spree::Money do
it "can get cents" do
money = Spree::Money.new(10)
expect(money.cents).to eq(1000)
expect(money.money.cents).to eq(1000)
end
context "with currency" do

View File

@@ -282,11 +282,6 @@ RSpec.describe Spree::Order do
let(:payment) { build(:payment) }
before { allow(order).to receive_messages pending_payments: [payment], total: 10 }
it "returns false if no pending_payments available" do
allow(order).to receive_messages pending_payments: []
expect(order.process_payments!).to be_falsy
end
context "when the processing is sucessful" do
it "processes the payments" do
expect(payment).to receive(:process!)

View File

@@ -6,17 +6,6 @@ RSpec.describe 'Orders Cycles endpoint' do
let(:distributor) { create(:distributor_enterprise) }
let(:order_cycle) { create(:order_cycle, distributors: [distributor]) }
context "requesting the latest version" do
let(:path) { "/api/order_cycles/#{order_cycle.id}/products?distributor=#{distributor.id}" }
it "redirects to v0, preserving URL params" do
get path
expect(response).to redirect_to(
"/api/v0/order_cycles/#{order_cycle.id}/products?distributor=#{distributor.id}"
)
end
end
context "requesting a specific API version" do
let(:path) { "/api/v0/order_cycles/#{order_cycle.id}/products?distributor=#{distributor.id}" }

View File

@@ -3,16 +3,19 @@
require 'spec_helper'
RSpec.describe "/payment_gateways/taler/:id" do
it "completes the order", :vcr do
shop = create(:distributor_enterprise)
taler = Spree::PaymentMethod::Taler.create!(
let(:shop) { create(:distributor_enterprise) }
let(:taler) {
Spree::PaymentMethod::Taler.create!(
name: "Taler",
environment: "test",
distributors: [shop],
preferred_backend_url: "https://backend.demo.taler.net/instances/sandbox",
preferred_api_key: "sandbox",
)
order = create(:order_ready_for_confirmation, payment_method: taler)
}
let!(:order) { create(:order_ready_for_confirmation, payment_method: taler) }
it "completes the order", :vcr do
payment = Spree::Payment.last
payment.update!(
source: taler,
@@ -30,4 +33,65 @@ RSpec.describe "/payment_gateways/taler/:id" do
payment.reload
expect(payment.state).to eq "completed"
end
it "redirects when payment invalid" do
payment = Spree::Payment.last
payment.update!(
source: taler,
payment_method: taler,
state: "processing", # invalid state to start processing again
)
get payment_gateways_confirm_taler_path(payment_id: payment.id)
expect(response).to redirect_to "/checkout/payment"
payment.reload
expect(payment.state).to eq "processing"
order.reload
expect(order.state).to eq "confirmation"
end
it "redirects when payment failed" do
payment = Spree::Payment.last
payment.update!(
source: taler,
payment_method: taler,
response_code: "2026.020-03R3ETNZZ0DVA",
)
allow_any_instance_of(Taler::Order)
.to receive(:fetch).with("order_status").and_return("claimed")
get payment_gateways_confirm_taler_path(payment_id: payment.id)
expect(response).to redirect_to "/checkout/payment"
payment.reload
expect(payment.state).to eq "failed"
order.reload
expect(order.state).to eq "confirmation"
end
it "handles all variants going out of stock" do
payment = Spree::Payment.last
payment.update!(
source: taler,
payment_method: taler,
response_code: "2026.020-03R3ETNZZ0DVA",
)
allow_any_instance_of(Taler::Order)
.to receive(:fetch).with("order_status").and_return("paid")
order.line_items[0].variant.on_hand = 0
get payment_gateways_confirm_taler_path(payment_id: payment.id)
expect(response).to redirect_to "/checkout/details"
payment.reload
expect(payment.state).to eq "completed"
order.reload
expect(order.state).to eq "confirmation"
end
end

View File

@@ -152,7 +152,7 @@ RSpec.configure do |config|
# Reset locale for all specs.
config.around(:each) do |example|
locale = ENV.fetch('LOCALE', 'en_TST')
locale = OpenFoodNetwork::I18nConfig.default_locale
I18n.with_locale(locale) { example.run }
end

View File

@@ -86,7 +86,7 @@ module StripeStubs
def stub_capture_request(order, response_mock)
stub_request(:post, "https://api.stripe.com/v1/payment_intents/pi_123/capture")
.with(body: { amount_to_capture: Spree::Money.new(order.total).cents },
.with(body: { amount_to_capture: Spree::Money.new(order.total).money.cents },
headers: { 'Stripe-Account' => 'abc123' })
.to_return(response_mock)
end

View File

@@ -27,10 +27,6 @@ module WebHelper
yield
end
def set_i18n_locale(locale = 'en')
page.execute_script("I18n.locale = '#{locale}'")
end
def pick_i18n_locale
page.evaluate_script("I18n.locale;")
end

View File

@@ -664,16 +664,16 @@ RSpec.describe '
end
it "finally, can invite unregistered users" do
within ".reveal-modal" do
tomselect_fill_in "user_invitation[email]", with: "email@email.com"
expect do
within ".reveal-modal" do
tomselect_fill_in "user_invitation[email]", with: "email@email.com"
expect do
click_button "Invite"
end.to enqueue_job(ActionMailer::MailDeliveryJob).exactly(:twice)
end
end
expect(page)
.to have_content "email@email.com has been invited to manage this enterprise"
expect(page)
.to have_content "email@email.com has been invited to manage this enterprise"
end.to enqueue_job(ActionMailer::MailDeliveryJob).exactly(:twice)
end
end
end

View File

@@ -10,17 +10,12 @@ RSpec.describe 'Multilingual' do
before do
login_as admin_user
visit spree.admin_dashboard_path
end
it 'has three locales available' do
expect(Rails.application.config.i18n[:default_locale]).to eq 'en'
expect(Rails.application.config.i18n[:locale]).to eq 'en'
expect(Rails.application.config.i18n[:available_locales]).to eq ['en', 'es', 'pt']
end
it 'can switch language by params' do
expect(pick_i18n_locale).to eq 'en'
visit spree.admin_dashboard_path
expect(pick_i18n_locale).to eq 'en_TST'
expect(get_i18n_translation('spree_admin_overview_enterprises_header')).to eq 'My Enterprises'
expect(page).to have_content 'My Enterprises'
expect(admin_user.locale).to be_nil
@@ -36,9 +31,9 @@ RSpec.describe 'Multilingual' do
it 'fallbacks to default_locale' do
visit spree.admin_dashboard_path(locale: 'it')
expect(pick_i18n_locale).to eq 'en'
expect(pick_i18n_locale).to eq 'en_TST'
expect(get_i18n_translation('spree_admin_overview_enterprises_header')).to eq 'My Enterprises'
expect(page).to have_content 'My Enterprises'
expect(admin_user.locale).to be_nil
expect(admin_user.reload.locale).to be_nil
end
end

View File

@@ -5,201 +5,192 @@ require "system_helper"
RSpec.describe "Managing users" do
include AuthenticationHelper
context "as super-admin" do
before do
login_as_admin
end
let(:admin_user) { create(:admin_user) }
context "from the index page" do
before do
create(:user, email: "a@example.com")
create(:user, email: "b@example.com")
before do
login_as admin_user
end
context "from the index page" do
let!(:user_a) { create(:user, email: "a@example.com") }
let!(:user_b) { create(:user, email: "b@example.com") }
context "searching users" do
it "should display the correct results for a user search" do
visit spree.admin_dashboard_path
click_link "Users"
end
context "users index page with sorting" do
before(:each) do
click_link "users_email_title"
end
it "should list users with order email asc" do
expect(page).to have_css('table#listing_users')
within("table#listing_users") do
expect(page).to have_content("a@example.com")
expect(page).to have_content("b@example.com")
end
end
it "should list users with order email desc" do
click_link "users_email_title"
within("table#listing_users") do
expect(page).to have_content("a@example.com")
expect(page).to have_content("b@example.com")
end
fill_in "q_email_cont", with: "a@example"
click_button "Search"
within("table#listing_users") do
expect(page).to have_content("a@example")
expect(page).not_to have_content("b@example")
end
end
end
context "searching users" do
it "should display the correct results for a user search" do
fill_in "q_email_cont", with: "a@example"
click_button "Search"
within("table#listing_users") do
expect(page).to have_content("a@example")
expect(page).not_to have_content("b@example")
end
end
context "editing users" do
it "should allow editing the user password" do
visit spree.admin_users_path
click_link("a@example.com")
fill_in "user_password", with: "welcome"
fill_in "user_password_confirmation", with: "welcome"
click_button "Update"
expect(page).to have_content("Account updated")
expect(current_path).to eq spree.edit_admin_user_path(user_a)
end
context "editing users" do
before(:each) do
click_link("a@example.com")
end
it "allows to change your own password without logging you out" do
visit spree.edit_admin_user_path(admin_user)
it "should allow editing the user password" do
fill_in "user_password", with: "welcome"
fill_in "user_password_confirmation", with: "welcome"
click_button "Update"
fill_in "user_password", with: "welcome"
fill_in "user_password_confirmation", with: "welcome"
click_button "Update"
expect(page).to have_content("Account updated")
end
expect(page).to have_content("Account updated")
expect(current_path).to eq spree.edit_admin_user_path(admin_user)
end
it "should let me edit the user email" do
fill_in "Email", with: "newemail@example.org"
click_button "Update"
it "should let me edit the user email" do
visit spree.edit_admin_user_path(user_a)
expect(page).to have_content("The account will be updated once " \
"the new email is confirmed.")
end
fill_in "Email", with: "newemail@example.org"
click_button "Update"
it "should allow to generate, regenarate and clear the user api key" do
user = Spree::User.find_by(email: "a@example.com")
expect(page).to have_content "NO KEY"
expect(page).to have_content("The account will be updated once " \
"the new email is confirmed.")
end
click_button "Generate API key"
first_user_api_key = user.reload.spree_api_key
expect(page).to have_content first_user_api_key
it "should allow to generate, regenarate and clear the user api key" do
visit spree.edit_admin_user_path(user_a)
click_button "Regenerate Key"
second_user_api_key = user.reload.spree_api_key
expect(page).to have_content second_user_api_key
expect(second_user_api_key).not_to eq first_user_api_key
expect(page).to have_content "NO KEY"
click_button "Clear key"
expect(page).to have_content "NO KEY"
end
click_button "Generate API key"
first_user_api_key = user_a.reload.spree_api_key
expect(page).to have_content first_user_api_key
it "should allow to disable the user and to enable it" do
expect(page).to have_unchecked_field "Disabled"
check "Disabled"
click_button "Update"
click_button "Regenerate Key"
second_user_api_key = user_a.reload.spree_api_key
expect(page).to have_content second_user_api_key
expect(second_user_api_key).not_to eq first_user_api_key
expect(page).to have_content("Account updated")
expect(page).to have_checked_field "Disabled"
uncheck "Disabled"
click_button "Update"
click_button "Clear key"
expect(page).to have_content "NO KEY"
end
expect(page).to have_content("Account updated")
expect(page).to have_unchecked_field "Disabled"
end
it "should allow to disable the user and to enable it" do
visit spree.edit_admin_user_path(user_a)
it "should toggle the api key generation view" do
user = Spree::User.find_by(email: "a@example.com")
expect(page).to have_unchecked_field "Disabled"
check "Disabled"
click_button "Update"
expect(page).to have_content "NO KEY"
expect {
click_button("Generate API key")
expect(page).to have_content("Key generated")
}.to change { user.reload.spree_api_key }.from(nil)
expect(page).to have_content("Account updated")
expect(page).to have_checked_field "Disabled"
uncheck "Disabled"
click_button "Update"
expect(page).to have_content("Account updated")
expect(page).to have_unchecked_field "Disabled"
end
it "should toggle the api key generation view" do
visit spree.edit_admin_user_path(user_a)
expect(page).to have_content "NO KEY"
expect {
click_button("Generate API key")
expect(page).to have_content("Key generated")
}.to change { user_a.reload.spree_api_key }.from(nil)
expect(page).to have_unchecked_field "Show API key view for user"
expect {
check "Show API key view for user"
expect(page).to have_content("Show API key view has been changed!")
expect(page).to have_checked_field "Show API key view for user"
}.to change { user_a.reload.show_api_key_view }.from(false).to(true)
expect {
uncheck "Show API key view for user"
expect(page).to have_content("Show API key view has been changed!")
expect(page).to have_unchecked_field "Show API key view for user"
expect {
check "Show API key view for user"
expect(page).to have_content("Show API key view has been changed!")
expect(page).to have_checked_field "Show API key view for user"
}.to change { user.reload.show_api_key_view }.from(false).to(true)
expect {
uncheck "Show API key view for user"
expect(page).to have_content("Show API key view has been changed!")
expect(page).to have_unchecked_field "Show API key view for user"
}.to change { user.reload.show_api_key_view }.to(false)
end
end
context "pagination" do
before do
# creates 8 more users
8.times { create(:user) }
expect(Spree::User.count).to eq 11
visit spree.admin_users_path
end
it "displays pagination" do
# table displays 10 entries
within('tbody') do
expect(page).to have_css('tr', count: 10)
end
within ".pagination" do
expect(page).not_to have_content "Previous"
expect(page).to have_content "Next"
click_on "2"
end
# table displays 1 entry
within('tbody') do
expect(page).to have_css('tr', count: 1)
end
within ".pagination" do
expect(page).to have_content "Previous"
expect(page).not_to have_content "Next"
end
end
}.to change { user_a.reload.show_api_key_view }.to(false)
end
end
describe "creating a user" do
it "confirms successful creation" do
visit spree.new_admin_user_path
# shows no confirmation message to start with
expect(page).not_to have_text "Email confirmation is pending"
fill_in "Email", with: "user1@example.org"
fill_in "Password", with: "user1Secret"
fill_in "Confirm Password", with: "user1Secret"
expect(page).to have_select "Language", selected: "English"
select "Español", from: "Language"
perform_enqueued_jobs do
expect do
click_button "Create"
end.to change { Spree::User.count }.by 1
expect(page).to have_text "Created Successfully"
expect(page).to have_text "Email confirmation is pending"
expect(Spree::User.last.locale).to eq "es"
expect(ActionMailer::Base.deliveries.first.subject).to match(
"Por favor, confirma tu cuenta de OFN"
)
end
context "pagination" do
before do
# creates 8 more users
8.times { create(:user) }
expect(Spree::User.count).to eq 11
visit spree.admin_users_path
end
end
describe "resending confirmation email" do
let(:user) { create :user, confirmed_at: nil }
it "displays success" do
visit spree.edit_admin_user_path user
expect do
# The `a` element doesn't have an href, so we can't use click_link.
find("a", text: "Resend").click
expect(page).to have_text "Resend done"
end.to enqueue_job ActionMailer::MailDeliveryJob
it "displays pagination" do
# table displays 10 entries
within('tbody') do
expect(page).to have_css('tr', count: 10)
end
within ".pagination" do
expect(page).not_to have_content "Previous"
expect(page).to have_content "Next"
click_on "2"
end
# table displays 1 entry
within('tbody') do
expect(page).to have_css('tr', count: 1)
end
within ".pagination" do
expect(page).to have_content "Previous"
expect(page).not_to have_content "Next"
end
end
end
end
describe "creating a user" do
it "confirms successful creation" do
visit spree.new_admin_user_path
# shows no confirmation message to start with
expect(page).not_to have_text "Email confirmation is pending"
fill_in "Email", with: "user1@example.org"
fill_in "Password", with: "user1Secret"
fill_in "Confirm Password", with: "user1Secret"
expect(page).to have_select "Language", selected: "English"
select "Español", from: "Language"
perform_enqueued_jobs do
expect do
click_button "Create"
end.to change { Spree::User.count }.by 1
expect(page).to have_text "Created Successfully"
expect(page).to have_text "Email confirmation is pending"
expect(Spree::User.last.locale).to eq "es"
expect(ActionMailer::Base.deliveries.first.subject).to match(
"Por favor, confirma tu cuenta de OFN"
)
end
end
end
describe "resending confirmation email" do
let(:user) { create :user, confirmed_at: nil }
it "displays success" do
visit spree.edit_admin_user_path user
expect do
# The `a` element doesn't have an href, so we can't use click_link.
find("a", text: "Resend").click
expect(page).to have_text "Resend done"
end.to enqueue_job ActionMailer::MailDeliveryJob
end
end
end

View File

@@ -273,7 +273,7 @@ RSpec.describe "Authentication" do
expect_logged_in
expect(page).to have_content 'SHOP NOW'
expect(user.reload.locale).to eq "en"
expect(user.reload.locale).to eq "en_TST"
end
end

View File

@@ -49,7 +49,6 @@ RSpec.describe "As a consumer, I want to checkout my order" do
before do
login_as(user)
visit checkout_path
end
context "payment step" do
@@ -67,6 +66,7 @@ RSpec.describe "As a consumer, I want to checkout my order" do
context "with a transaction fee" do
before do
visit checkout_path
click_button "Next - Order summary"
end
@@ -84,6 +84,7 @@ RSpec.describe "As a consumer, I want to checkout my order" do
context "after completing the order" do
before do
visit checkout_path
click_on "Complete order"
end
it_behaves_like "displays the transaction fee", "order confirmation"
@@ -277,7 +278,7 @@ RSpec.describe "As a consumer, I want to checkout my order" do
describe "choosing" do
shared_examples "different payment methods" do |pay_method|
context "checking out with #{pay_method}", if: pay_method.eql?("Stripe SCA") == false do
context "checking out with #{pay_method}" do
before do
visit checkout_step_path(:payment)
end
@@ -291,46 +292,9 @@ RSpec.describe "As a consumer, I want to checkout my order" do
expect(order.reload.state).to eq "complete"
end
end
context "for Stripe SCA", if: pay_method.eql?("Stripe SCA") do
around do |example|
with_stripe_setup { example.run }
end
before do
stripe_enable
visit checkout_step_path(:payment)
end
it "selects Stripe SCA and proceeds to the summary step" do
choose pay_method.to_s
fill_out_card_details
click_on "Next - Order summary"
proceed_to_summary
end
context "when saving card" do
it "selects Stripe SCA and proceeds to the summary step" do
stub_customers_post_request(email: order.user.email)
stub_payment_method_attach_request
choose pay_method.to_s
fill_out_card_details
check "Save card for future use"
click_on "Next - Order summary"
proceed_to_summary
# Verify card has been saved with correct stripe IDs
user_credit_card = order.reload.user.credit_cards.first
expect(user_credit_card.gateway_payment_profile_id).to eq "pm_123"
expect(user_credit_card.gateway_customer_profile_id).to eq "cus_A123"
end
end
end
end
describe "shared examples" do
describe "payment method" do
let!(:cash) { create(:payment_method, distributors: [distributor], name: "Cash") }
context "Cash" do
@@ -365,7 +329,40 @@ RSpec.describe "As a consumer, I want to checkout my order" do
create(:stripe_sca_payment_method, distributors: [distributor], name: "Stripe SCA")
}
it_behaves_like "different payment methods", "Stripe SCA"
around do |example|
with_stripe_setup { example.run }
end
before do
stripe_enable
visit checkout_step_path(:payment)
end
it "selects Stripe SCA and proceeds to the summary step" do
choose "Stripe SCA"
fill_out_card_details
click_on "Next - Order summary"
proceed_to_summary
end
context "when saving card" do
it "selects Stripe SCA and proceeds to the summary step" do
stub_customers_post_request(email: order.user.email)
stub_payment_method_attach_request
choose "Stripe SCA"
fill_out_card_details
check "Save card for future use"
click_on "Next - Order summary"
proceed_to_summary
# Verify card has been saved with correct stripe IDs
user_credit_card = order.reload.user.credit_cards.first
expect(user_credit_card.gateway_payment_profile_id).to eq "pm_123"
expect(user_credit_card.gateway_customer_profile_id).to eq "cus_A123"
end
end
end
context "Taler" do

View File

@@ -5,189 +5,72 @@ require 'system_helper'
RSpec.describe 'Multilingual' do
include AuthenticationHelper
include WebHelper
include ShopWorkflow
include UIComponentHelper
include CookieHelper
let(:user) { create(:user) }
it 'has three locales available' do
expect(Rails.application.config.i18n[:default_locale]).to eq 'en'
expect(Rails.application.config.i18n[:locale]).to eq 'en'
expect(Rails.application.config.i18n[:available_locales]).to eq ['en', 'es', 'pt']
expect(Rails.application.config.i18n[:default_locale]).to eq 'en_TST'
expect(Rails.application.config.i18n[:locale]).to eq 'en_TST'
expect(Rails.application.config.i18n[:available_locales]).to eq ['en_TST', 'es', 'pt', 'en']
end
it '18n-js fallsback to default language' do
# in backend it doesn't until we change enforce_available_locales to `true`
it 'can switch language by params' do
visit root_path
set_i18n_locale('it')
expect(pick_i18n_locale).to eq 'en_TST'
expect(get_i18n_translation('label_shops')).to eq 'Shops'
expect(cookies_name).not_to include('locale')
expect(page).to have_content 'SHOPS'
visit root_path(locale: 'es')
expect(pick_i18n_locale).to eq 'es'
expect(get_i18n_translation('label_shops')).to eq 'Tiendas'
expect_menu_and_cookie_in_es
# it is not in the list of available of available_locales
visit root_path(locale: 'it')
expect(pick_i18n_locale).to eq 'es'
expect(get_i18n_translation('label_shops')).to eq 'Tiendas'
expect_menu_and_cookie_in_es
end
context 'can switch language by params' do
it 'in root path' do
visit root_path
expect(pick_i18n_locale).to eq 'en'
expect(get_i18n_translation('label_shops')).to eq 'Shops'
expect(cookies_name).not_to include('locale')
expect(page).to have_content 'SHOPS'
it 'updates user locale from cookie if it is empty' do
visit root_path(locale: 'es')
visit root_path(locale: 'es')
expect(pick_i18n_locale).to eq 'es'
expect(get_i18n_translation('label_shops')).to eq 'Tiendas'
expect_menu_and_cookie_in_es
expect_menu_and_cookie_in_es
expect(user.locale).to be_nil
login_as user
visit root_path
# it is not in the list of available of available_locales
visit root_path(locale: 'it')
expect(pick_i18n_locale).to eq 'es'
expect(get_i18n_translation('label_shops')).to eq 'Tiendas'
expect_menu_and_cookie_in_es
end
expect_menu_and_cookie_in_es
context 'with a product in the cart' do
let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) }
let!(:order_cycle) {
create(:simple_order_cycle, distributors: [distributor], variants: [product.variants.first])
}
let(:product) { create(:simple_product) }
let(:order) { create(:order, order_cycle:, distributor:) }
# The user's locale is not changed if the language was chosen before
# login. Is it a bug or a feature? Probably not important...
expect(user.reload.locale).to eq nil
before do
pick_order order
add_product_to_cart order, product, quantity: 1
end
visit root_path(locale: 'es')
expect(user.reload.locale).to eq 'es'
it "in the cart page" do
visit main_app.cart_path(locale: 'es')
logout
expect_menu_and_cookie_in_es
expect(page).to have_content 'Precio'
end
it "visiting checkout as a guest user" do
visit checkout_path(locale: 'es')
expect_menu_and_cookie_in_es
expect(page).to have_content 'Iniciar sesión'
end
end
expect_menu_and_cookie_in_es
expect(page).to have_content '¿Estás interesada en entrar en Open Food Network?'
end
context 'with user' do
let(:user) { create(:user) }
it "allows switching language via the main navigation" do
visit root_path
it 'updates user locale from cookie if it is empty' do
visit root_path(locale: 'es')
expect(page).to have_content 'SHOPS'
expect_menu_and_cookie_in_es
expect(user.locale).to be_nil
login_as user
visit root_path
find('.language-switcher').click
within '.language-switcher .dropdown' do
expect(page).not_to have_link 'English'
expect(page).to have_link 'Español'
expect_menu_and_cookie_in_es
click_link 'Español'
end
it 'updates user locale and stays in cookie after logout' do
login_as user
visit root_path(locale: 'es')
user.reload
expect(user.locale).to eq 'es'
logout
expect_menu_and_cookie_in_es
expect(page).to have_content '¿Estás interesada en entrar en Open Food Network?'
end
context "visiting checkout as logged user" do
let!(:zone) { create(:zone_with_member) }
let(:supplier) { create(:supplier_enterprise) }
let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) }
let(:product) {
create(:taxed_product, supplier_id: supplier.id, price: 10, zone:)
}
let(:variant) { product.variants.first }
let!(:order_cycle) {
create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor],
coordinator: create(:distributor_enterprise),
variants: [variant])
}
let(:free_shipping) {
create(:shipping_method, require_ship_address: false)
}
let!(:payment) {
create(:payment_method, distributors: [distributor],
name: "Payment")
}
let(:order) {
create(:order_ready_for_confirmation, distributor:)
}
before do
pick_order order
login_as user
end
it "on the details step" do
visit checkout_step_path(:details, locale: 'es')
expect_menu_and_cookie_in_es
expect(page).to have_content "Sus detalles"
end
it "on the payment step" do
visit checkout_step_path(:payment, locale: 'es')
expect_menu_and_cookie_in_es
expect(page).to have_content "Puede revisar y confirmar su pedido"
end
it "on the summary step" do
visit checkout_step_path(:summary, locale: 'es')
expect_menu_and_cookie_in_es
expect(page).to have_content "Detalles de entrega"
end
end
end
describe "using the language switcher UI" do
context "when there is only one language available" do
before do
allow(ENV).to receive(:[]).and_call_original
allow(ENV).to receive(:[]).with("LOCALE").and_return("en")
allow(ENV).to receive(:[]).with("AVAILABLE_LOCALES").and_return("en")
end
it "hides the dropdown language menu" do
visit root_path
expect(page).not_to have_css 'ul.right li.language-switcher.has-dropdown'
end
end
context "when there are multiple languages available" do
before do
allow(ENV).to receive(:[]).and_call_original
allow(ENV).to receive(:[]).with("LOCALE").and_return("en")
allow(ENV).to receive(:[]).with("AVAILABLE_LOCALES").and_return("en,es")
end
it "allows switching language via the main navigation" do
visit root_path
expect(page).to have_content 'SHOPS'
find('.language-switcher').click
within '.language-switcher .dropdown' do
expect(page).not_to have_link 'English', href: '/locales/en'
expect(page).to have_link 'Español', href: '/locales/es'
find('li a[href="/locales/es"]').click
end
expect_menu_and_cookie_in_es
end
end
expect_menu_and_cookie_in_es
end
end

View File

@@ -3,221 +3,34 @@
require 'system_helper'
RSpec.describe "Check out with Stripe" do
include AuthenticationHelper
include ShopWorkflow
include CheckoutRequestsHelper
include StripeHelper
include StripeStubs
let(:distributor) { create(:distributor_enterprise) }
let!(:order_cycle) {
create(:simple_order_cycle, distributors: [distributor], variants: [variant])
}
let(:product) { create(:product, price: 10) }
let(:variant) { product.variants.first }
let(:order) {
create(:order, order_cycle:, distributor:, bill_address_id: nil,
ship_address_id: nil)
}
let(:shipping_with_fee) {
create(:shipping_method, require_ship_address: false, name: "Donkeys",
calculator: Calculator::FlatRate.new(preferred_amount: 4.56))
}
let(:free_shipping) { create(:shipping_method) }
let!(:check_with_fee) {
create(:payment_method, distributors: [distributor],
calculator: Calculator::FlatRate.new(preferred_amount: 5.67))
}
around do |example|
with_stripe_setup { example.run }
end
before do
stripe_enable
pick_order order
add_product_to_cart order, product
distributor.shipping_methods << [shipping_with_fee, free_shipping]
end
pending "using Stripe SCA" do
let!(:stripe_account) { create(:stripe_account, enterprise: distributor) }
let!(:stripe_sca_payment_method) {
create(:stripe_sca_payment_method, distributors: [distributor])
}
let!(:shipping_method) { create(:shipping_method) }
let(:error_message) { "Card was declined: insufficient funds." }
before do
stub_payment_intent_get_request
stub_payment_methods_post_request
end
describe "using Stripe SCA" do
context "with guest checkout" do
before do
stub_retrieve_payment_method_request("pm_123")
stub_list_customers_request(email: order.user.email, response: {})
stub_get_customer_payment_methods_request(customer: "cus_A456", response: {})
end
context "when the card is accepted" do
before do
stub_payment_intents_post_request(order:)
stub_successful_capture_request order:
end
it "completes checkout successfully" do
checkout_with_stripe
expect(page).to have_content "Confirmed"
expect(page.find("#amount-paid").text).to have_content "$19.99"
expect(order.reload.completed?).to eq true
expect(order.payments.first.state).to eq "completed"
end
it "completes checkout successfully"
end
context "when the card is rejected" do
before do
stub_payment_intents_post_request(order:)
stub_failed_capture_request order:, response: { message: error_message }
end
it "shows an error message from the Stripe response" do
checkout_with_stripe
expect(page).to have_content error_message
expect(order.reload.state).to eq "cart"
expect(order.payments.first.state).to eq "failed"
end
it "shows an error message from the Stripe response"
end
context "when the card needs extra SCA authorization" do
before do
stripe_redirect_url = checkout_path(payment_intent: "pi_123")
stub_payment_intents_post_request_with_redirect order:,
redirect_url: stripe_redirect_url
end
describe "and the authorization succeeds" do
before do
stub_successful_capture_request order:
end
it "completes checkout successfully" do
checkout_with_stripe
# We make stripe return stripe_redirect_url (which is already sending the user back
# to the checkout) as if the authorization was done. We can then control the actual
# authorization or failure of the payment through the mock
# stub_successful_capture_request
expect(page).to have_content "Confirmed"
expect(order.reload.completed?).to eq true
expect(order.payments.first.state).to eq "completed"
end
it "completes checkout successfully"
end
describe "and the authorization fails" do
before do
stub_failed_capture_request order:, response: { message: error_message }
end
it "shows an error message from the Stripe response" do
checkout_with_stripe
# We make stripe return stripe_redirect_url (which is already sending the user back to
# the checkout) as if the authorization was done. We can then control the actual
# authorization or failure of the payment through the mock stub_failed_capture_request
expect(page).to have_content error_message
expect(order.reload.state).to eq "cart"
expect(order.payments.first.state).to eq "failed"
end
it "shows an error message from the Stripe response"
end
end
context "with multiple payment attempts; one failed and one succeeded" do
before do
stub_payment_intents_post_request order:
end
it "records failed payment attempt and allows order completion" do
# First payment attempt is rejected
stub_failed_capture_request(order:, response: { message: error_message })
checkout_with_stripe
expect(page).to have_content error_message
expect(order.reload.payments.count).to eq 1
expect(order.state).to eq "cart"
expect(order.payments.first.state).to eq "failed"
# Second payment attempt is accepted
stub_successful_capture_request(order:)
place_order
expect(page).to have_content "Confirmed"
expect(order.reload.payments.count).to eq 2
expect(order.state).to eq "complete"
expect(order.payments.last.state).to eq "completed"
end
it "records failed payment attempt and allows order completion"
end
end
context "with a logged in user" do
let(:user) { order.user }
before do
login_as user
end
context "saving a card and re-using it" do
before do
stub_retrieve_payment_method_request("pm_123")
stub_list_customers_request(email: order.user.email, response: {})
stub_get_customer_payment_methods_request(customer: "cus_A456", response: {})
stub_get_customer_payment_methods_request(customer: "cus_A123", response: {})
stub_payment_methods_post_request(
request: { payment_method: "pm_123", customer: "cus_A123" },
response: { pm_id: "pm_123" }
)
stub_add_metadata_request(payment_method: "pm_123", response: {})
stub_payment_intents_post_request(order:)
stub_successful_capture_request(order:)
stub_customers_post_request email: "test@test.com" # First checkout with default details
stub_customers_post_request email: user.email # Second checkout with saved user details
stub_payment_method_attach_request
end
it "allows saving a card and re-using it" do
checkout_with_stripe guest_checkout: false, remember_card: true
expect(page).to have_content "Confirmed"
expect(order.reload.completed?).to eq true
expect(order.payments.first.state).to eq "completed"
# Verify card has been saved with correct stripe IDs
user_credit_card = order.reload.user.credit_cards.first
expect(user_credit_card.gateway_payment_profile_id).to eq "pm_123"
expect(user_credit_card.gateway_customer_profile_id).to eq "cus_A123"
# Prepare a second order
new_order = create(:order, user:, order_cycle:,
distributor:, bill_address_id: nil,
ship_address_id: nil)
pick_order(new_order)
add_product_to_cart(new_order, product, quantity: 10)
stub_payment_intents_post_request order: new_order
stub_successful_capture_request order: new_order
# Checkout with saved card
visit checkout_path
choose free_shipping.name
choose stripe_sca_payment_method.name
expect(page).to have_content "Use a saved card"
expect(page).to have_select 'selected_card', selected: "Visa x-4242 Exp:10/2050"
place_order
end
it "allows saving a card and re-using it"
end
end
end

929
yarn.lock

File diff suppressed because it is too large Load Diff