Skip to content

Commit e3d7ccf

Browse files
authored
Merge branch 'trunk' into woomob-870-shipping-labels-validate-dimensions-for-custom-packages
2 parents 0deef46 + 57650fa commit e3d7ccf

File tree

55 files changed

+1659
-1279
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1659
-1279
lines changed

.github/workflows/screenshots.yml

Lines changed: 38 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@ on:
55
types: [opened, synchronize, reopened, labeled, unlabeled]
66

77
env:
8-
S3_BUCKET: ${{ secrets.S3_BUCKET }}
9-
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
10-
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
118
CONFIGURE_ENCRYPTION_KEY: ${{ secrets.CONFIGURE_ENCRYPTION_KEY }}
129

1310
jobs:
1411
build:
1512
name: Build Application
1613
if: contains(github.event.pull_request.labels.*.name, 'generate screenshots')
17-
runs-on: macos-latest
14+
runs-on: macos-15
1815

1916
steps:
2017
- name: "Check out Project"
@@ -30,26 +27,21 @@ jobs:
3027
- name: Compile the App
3128
run: bundle exec fastlane build_screenshots
3229

33-
- name: Archive App
30+
- name: Archive Build Products
3431
uses: actions/upload-artifact@v4
3532
with:
36-
name: screenshot-app
37-
path: fastlane/DerivedData/Build/Products/Debug-iphonesimulator/WooCommerce.app
38-
39-
- name: Archive Runner
40-
uses: actions/upload-artifact@v4
41-
with:
42-
name: screenshot-runner
43-
path: fastlane/DerivedData/Build/Products/Debug-iphonesimulator/WooCommerceScreenshots-Runner.app
33+
name: screenshot-build-products
34+
path: fastlane/DerivedData/Build/Products/Debug-iphonesimulator/
35+
retention-days: 1
4436

4537
capture:
4638
name: Capture
4739
needs: build
48-
runs-on: macos-latest
40+
runs-on: macos-15
4941

5042
strategy:
5143
matrix:
52-
language: [ar, de-DE, en-US, es-ES, fr-FR, he, id, it, ja, ko, nl-NL, pt-BR, ru, sv, tr, zh-Hans, zh-Hant]
44+
language: [ar-SA, de-DE, en-US, es-ES, fr-FR, he, id, it, ja, ko, nl-NL, pt-BR, ru, sv, tr, zh-Hans, zh-Hant]
5345
mode: [dark, light]
5446
steps:
5547
- uses: actions/checkout@v4
@@ -61,17 +53,11 @@ jobs:
6153
- name: Install Fastlane Dependencies
6254
run: bundle exec fastlane run configure_apply
6355

64-
- name: Download Screenshot App
65-
uses: actions/download-artifact@v4
66-
with:
67-
name: screenshot-app
68-
path: fastlane/DerivedData/Build/Products/Debug-iphonesimulator/WooCommerce.app
69-
70-
- name: Download Screenshot Runner
56+
- name: Download Build Products
7157
uses: actions/download-artifact@v4
7258
with:
73-
name: screenshot-runner
74-
path: fastlane/DerivedData/Build/Products/Debug-iphonesimulator/WooCommerceScreenshots-Runner.app
59+
name: screenshot-build-products
60+
path: fastlane/DerivedData/Build/Products/Debug-iphonesimulator/
7561

7662
- name: Generate Screenshots
7763
run: |
@@ -85,14 +71,18 @@ jobs:
8571
path: fastlane/logs
8672

8773
- name: Archive Generated Screenshots
88-
run: |
89-
cd fastlane && mkdir $GITHUB_RUN_ID && mv screenshots $GITHUB_RUN_ID
90-
aws s3 cp $GITHUB_RUN_ID s3://$S3_BUCKET/$GITHUB_RUN_ID --recursive --exclude "*.html"
74+
uses: actions/upload-artifact@v4
75+
with:
76+
name: "screenshots-${{ matrix.language }}-${{ matrix.mode }}"
77+
path: fastlane/screenshots/
9178

9279
process:
9380
name: "Process Screenshots"
9481
needs: capture
95-
runs-on: macos-latest
82+
runs-on: macos-15
83+
84+
env:
85+
BUNDLE_WITH: screenshots
9686

9787
steps:
9888
- uses: actions/checkout@v4
@@ -101,55 +91,50 @@ jobs:
10191
run: |
10292
brew install imagemagick@7
10393
brew link imagemagick@7 --force
94+
brew install automattic/build-tools/drawText
10495
10596
- name: "Set up Ruby"
10697
uses: ruby/setup-ruby@v1
10798
with:
10899
bundler-cache: true
109100

110-
- name: Install Screenshot Gems
111-
run: bundle install --with screenshots
112-
113101
- name: Install Fastlane Dependencies
114102
run: bundle exec fastlane run configure_apply
115103

116104
- name: Download Generated Screenshots
117-
run: |
118-
cd fastlane
119-
aws s3 cp s3://$S3_BUCKET/$GITHUB_RUN_ID/screenshots screenshots/ --recursive --exclude "*.html"
105+
uses: actions/download-artifact@v4
106+
with:
107+
pattern: "screenshots-*"
108+
path: fastlane/screenshots/
109+
merge-multiple: true
120110

121-
- name: Generate and Upload Screenshot Summary
111+
- name: Generate Screenshot Summary
122112
run: |
123113
bundle exec fastlane create_screenshot_summary
124-
aws s3 cp fastlane/screenshots/screenshots.html s3://$S3_BUCKET/$GITHUB_RUN_ID/screenshots/screenshots.html
114+
115+
- name: Upload Screenshot Summary
116+
uses: actions/upload-artifact@v4
117+
with:
118+
name: screenshot-summary
119+
path: fastlane/screenshots/screenshots.html
125120

126121
- name: Archive Raw Screenshots
127122
uses: actions/upload-artifact@v4
128123
with:
129124
name: raw-screenshots
130125
path: fastlane/screenshots
131126

132-
- name: Install Promo Screenshot Fonts
127+
- name: Setup Git LFS
133128
run: |
134-
aws s3 cp s3://$S3_BUCKET/fonts.zip fonts.zip
135-
unzip fonts.zip
136-
137-
mkdir -p ~/Library/Fonts
138-
cp -v fonts/*.otf ~/Library/Fonts
139-
ls ~/Library/Fonts
140-
141-
mkdir -p /Library/Fonts
142-
cp -v fonts/*.otf /Library/Fonts
143-
ls /Library/Fonts
144-
145-
# Reset the font server in order to use these fonts
146-
atsutil databases -removeUser
147-
atsutil server -shutdown
148-
atsutil server -ping
129+
# Install Git LFS if not available
130+
if ! command -v git-lfs &> /dev/null; then
131+
echo "Installing Git LFS..."
132+
brew install git-lfs
133+
fi
134+
git lfs install && git lfs fetch && git lfs pull
149135
150136
- name: Generate Promo Screenshots
151137
run: |
152-
git lfs install && git lfs fetch && git lfs pull
153138
bundle exec fastlane create_promo_screenshots force:true
154139
155140
- name: Archive Promo Screenshots

Modules/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ enum XcodeSupport {
342342
.xcodeTarget(
343343
XcodeTargetNames.notificationExtension,
344344
dependencies: [
345-
"Networking",
345+
"NetworkingCore",
346346
"WooFoundation",
347347
.product(name: "KeychainAccess", package: "KeychainAccess"),
348348
]

Modules/Sources/Experiments/DefaultFeatureFlagService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
9696
case .pointOfSaleAsATabi1:
9797
return true
9898
case .pointOfSaleAsATabi2:
99-
return buildConfig == .localDeveloper || buildConfig == .alpha
99+
return true
100100
case .pointOfSaleOrdersi1:
101101
return true
102102
case .pointOfSaleOrdersi2:

Modules/Sources/Networking/Remote/SiteSettingsRemote.swift

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import Foundation
22

3+
/// Protocol for SiteSettingsRemote to enable testing.
4+
public protocol SiteSettingsRemoteProtocol {
5+
func setFeature(for siteID: Int64, feature: SiteSettingsFeature, enabled: Bool) async throws -> Bool
6+
}
7+
38
/// Features that can be enabled/disabled in core, under WC Settings > Advanced > Features.
49
public enum SiteSettingsFeature {
510
case pointOfSale
@@ -114,16 +119,49 @@ public class SiteSettingsRemote: Remote {
114119
let mapper = SiteSettingMapper(siteID: siteID, settingsGroup: .advanced)
115120
let response = try await enqueue(request, mapper: mapper)
116121
switch response.value {
117-
case "yes":
122+
case Constants.featureEnabledValue:
123+
return true
124+
case Constants.featureDisabledValue:
125+
return false
126+
default:
127+
throw SiteSettingsRemoteError.invalidResponse
128+
}
129+
}
130+
131+
/// Enables or disables a specific feature in the site WC settings.
132+
///
133+
/// - Parameters:
134+
/// - siteID: Site for which we'll update the feature status.
135+
/// - feature: The feature to enable or disable.
136+
/// - enabled: Whether the feature should be enabled (true) or disabled (false).
137+
/// - Returns: A boolean indicating the updated feature status.
138+
/// - Throws: Error if the request fails.
139+
///
140+
public func setFeature(for siteID: Int64, feature: SiteSettingsFeature, enabled: Bool) async throws -> Bool {
141+
let value = enabled ? Constants.featureEnabledValue : Constants.featureDisabledValue
142+
let parameters: [String: Any] = [Constants.valueParameter: value]
143+
let path = Constants.siteSettingsPath + Constants.advancedSettingsGroup + "/woocommerce_feature_\(feature.rawValue)_enabled"
144+
let request = JetpackRequest(wooApiVersion: .mark3,
145+
method: .put,
146+
siteID: siteID,
147+
path: path,
148+
parameters: parameters,
149+
availableAsRESTRequest: true)
150+
let mapper = SiteSettingMapper(siteID: siteID, settingsGroup: .advanced)
151+
let response = try await enqueue(request, mapper: mapper)
152+
switch response.value {
153+
case Constants.featureEnabledValue:
118154
return true
119-
case "no":
155+
case Constants.featureDisabledValue:
120156
return false
121157
default:
122158
throw SiteSettingsRemoteError.invalidResponse
123159
}
124160
}
125161
}
126162

163+
extension SiteSettingsRemote: SiteSettingsRemoteProtocol {}
164+
127165
public extension SiteSettingsFeature {
128166
var rawValue: String {
129167
switch self {
@@ -142,6 +180,8 @@ private extension SiteSettingsRemote {
142180
static let productSettingsGroup: String = "products"
143181
static let advancedSettingsGroup: String = "advanced"
144182
static let valueParameter: String = "value"
183+
static let featureEnabledValue: String = "yes"
184+
static let featureDisabledValue: String = "no"
145185
}
146186
}
147187

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import Foundation
2+
import Networking
3+
4+
/// Protocol for POS site setting service that manages WooCommerce feature flags.
5+
public protocol POSSiteSettingServiceProtocol {
6+
/// Enables or disables a specific feature in the site WC settings.
7+
/// - Parameters:
8+
/// - siteID: The site ID for which to update the feature setting.
9+
/// - feature: The feature to enable or disable.
10+
/// - enabled: Whether the feature should be enabled (true) or disabled (false).
11+
/// - Returns: A boolean indicating the updated feature status.
12+
/// - Throws: Error if the request fails or the setting cannot be updated.
13+
func setFeature(siteID: Int64, feature: SiteSettingsFeature, enabled: Bool) async throws -> Bool
14+
}
15+
16+
/// Service for managing Point of Sale related site settings.
17+
public final class POSSiteSettingService: POSSiteSettingServiceProtocol {
18+
private let remote: SiteSettingsRemoteProtocol
19+
20+
public init(credentials: Credentials?) {
21+
let network = AlamofireNetwork(credentials: credentials)
22+
self.remote = SiteSettingsRemote(network: network)
23+
}
24+
25+
/// Test-friendly initializer that accepts a remote implementation.
26+
/// - Parameter remote: The remote service implementation to use for network requests.
27+
init(remote: SiteSettingsRemoteProtocol) {
28+
self.remote = remote
29+
}
30+
31+
public func setFeature(siteID: Int64, feature: SiteSettingsFeature, enabled: Bool) async throws -> Bool {
32+
try await remote.setFeature(for: siteID, feature: feature, enabled: enabled)
33+
}
34+
}

Modules/Tests/NetworkingTests/Remote/SiteSettingsRemoteTests.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,48 @@ final class SiteSettingsRemoteTests: XCTestCase {
228228
XCTAssertEqual(error as? NetworkError, .unacceptableStatusCode(statusCode: 500))
229229
}
230230
}
231+
232+
// MARK: - `setFeature`
233+
234+
func test_setFeature_enables_feature_when_enabled_is_true() async throws {
235+
// Given
236+
let remote = SiteSettingsRemote(network: network)
237+
network.simulateResponse(requestUrlSuffix: "settings/advanced/woocommerce_feature_point_of_sale_enabled",
238+
filename: "settings-advanced-feature-pos-enabled")
239+
240+
// When
241+
let isEnabled = try await remote.setFeature(for: sampleSiteID, feature: .pointOfSale, enabled: true)
242+
243+
// Then
244+
XCTAssertTrue(isEnabled)
245+
}
246+
247+
func test_setFeature_disables_feature_when_enabled_is_false() async throws {
248+
// Given
249+
let remote = SiteSettingsRemote(network: network)
250+
network.simulateResponse(requestUrlSuffix: "settings/advanced/woocommerce_feature_point_of_sale_enabled",
251+
filename: "settings-advanced-feature-pos-disabled")
252+
253+
// When
254+
let isEnabled = try await remote.setFeature(for: sampleSiteID, feature: .pointOfSale, enabled: false)
255+
256+
// Then
257+
XCTAssertFalse(isEnabled)
258+
}
259+
260+
func test_setFeature_throws_error_when_network_fails() async {
261+
// Given
262+
let remote = SiteSettingsRemote(network: network)
263+
let error = NetworkError.unacceptableStatusCode(statusCode: 500)
264+
network.simulateError(requestUrlSuffix: "settings/advanced/woocommerce_feature_point_of_sale_enabled",
265+
error: error)
266+
267+
// When/Then
268+
do {
269+
_ = try await remote.setFeature(for: sampleSiteID, feature: .pointOfSale, enabled: true)
270+
XCTFail("Expected error to be thrown")
271+
} catch {
272+
XCTAssertEqual(error as? NetworkError, .unacceptableStatusCode(statusCode: 500))
273+
}
274+
}
231275
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Networking
2+
3+
final class MockSiteSettingsRemote: SiteSettingsRemoteProtocol {
4+
var setFeatureCalled: Bool = false
5+
var spySetFeatureSiteID: Int64?
6+
var spySetFeatureFeature: SiteSettingsFeature?
7+
var spySetFeatureEnabled: Bool?
8+
var setFeatureResult: Result<Bool, Error> = .success(true)
9+
10+
func setFeature(for siteID: Int64, feature: SiteSettingsFeature, enabled: Bool) async throws -> Bool {
11+
setFeatureCalled = true
12+
spySetFeatureSiteID = siteID
13+
spySetFeatureFeature = feature
14+
spySetFeatureEnabled = enabled
15+
16+
switch setFeatureResult {
17+
case .success(let result):
18+
return result
19+
case .failure(let error):
20+
throw error
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)