mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
test: Add test for implicit evaluation loop (#19200)
This commit is contained in:
@@ -101,21 +101,63 @@ test.describe('Proxy tests @capability:proxy', () => {
|
||||
|
||||
The ProxyServer service supports recording HTTP requests for test mocking and replay. All proxied requests are automatically recorded by the mock server as described in the [Mock Server documentation](https://www.mock-server.com/proxy/record_and_replay.html).
|
||||
|
||||
```typescript
|
||||
// Record all requests
|
||||
await proxyServer.recordExpectations();
|
||||
#### Recording Expectations
|
||||
|
||||
// Record requests with matching criteria
|
||||
await proxyServer.recordExpectations({
|
||||
```typescript
|
||||
// Record all requests (the request is simplified/cleansed to method/path/body/query)
|
||||
await proxyServer.recordExpectations('test-folder');
|
||||
|
||||
// Record with filtering and options
|
||||
await proxyServer.recordExpectations('test-folder', {
|
||||
host: 'googleapis.com', // Filter by host (partial match)
|
||||
dedupe: true, // Remove duplicate requests
|
||||
raw: false // Save cleaned requests (default)
|
||||
});
|
||||
|
||||
// Record raw requests with all headers and metadata
|
||||
await proxyServer.recordExpectations('test-folder', {
|
||||
raw: true // Save complete original requests
|
||||
});
|
||||
|
||||
// Record requests matching specific criteria
|
||||
await proxyServer.recordExpectations('test-folder', {
|
||||
pathOrRequestDefinition: {
|
||||
method: 'POST',
|
||||
path: '/api/workflows',
|
||||
queryStringParameters: {
|
||||
'userId': ['123']
|
||||
path: '/api/workflows'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Recorded expectations are saved as JSON files in the `expectations/` directory with unique names based on the request details. When the ProxyServer fixture initializes, all saved expectations are automatically loaded and mocked for subsequent test runs.
|
||||
#### Loading and Using Recorded Expectations
|
||||
|
||||
Recorded expectations are saved as JSON files in the `expectations/` directory. To use them in tests, you must explicitly load them:
|
||||
|
||||
```typescript
|
||||
test('should use recorded expectations', async ({ proxyServer }) => {
|
||||
// Load expectations from a specific folder
|
||||
await proxyServer.loadExpectations('test-folder');
|
||||
|
||||
// Your test code here - requests will be mocked using loaded expectations
|
||||
});
|
||||
```
|
||||
|
||||
#### Important: Cleanup Expectations
|
||||
|
||||
**Remember to clean up expectations before or after test runs:**
|
||||
|
||||
```typescript
|
||||
test.beforeEach(async ({ proxyServer }) => {
|
||||
// Clear any existing expectations before test
|
||||
await proxyServer.clearAllExpectations();
|
||||
});
|
||||
|
||||
test.afterEach(async ({ proxyServer }) => {
|
||||
// Or clear expectations after test
|
||||
await proxyServer.clearAllExpectations();
|
||||
});
|
||||
```
|
||||
|
||||
This prevents expectations from one test affecting others and ensures test isolation.
|
||||
|
||||
## Writing Tests
|
||||
For guidelines on writing new tests, see [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "POST",
|
||||
"path": "/token"
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"X-XSS-Protection": ["0"],
|
||||
"X-Frame-Options": ["SAMEORIGIN"],
|
||||
"X-Content-Type-Options": ["nosniff"],
|
||||
"Vary": ["Origin", "X-Origin", "Referer"],
|
||||
"Server": ["scaffolding on HTTPServer2"],
|
||||
"Date": ["Thu, 04 Sep 2025 14:07:57 GMT"],
|
||||
"Content-Type": ["application/json; charset=UTF-8"],
|
||||
"Alt-Svc": ["h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"]
|
||||
},
|
||||
"body": {
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"access_token": "mock_access_token_fjWwvFFZMgzvj8i08j8coeyRZz_p57s_Vqjk3v0kzrmOr2MJMUVYYlOII5Zq8BFU08drBzsz50Q-6f3S1MBt2dO3vYkzw1Ml7jnykQmUSz1wVce-M9zefdaFVeVuc1rjIviipVeO3ojF95FvghMMwnVvUF55ppzI3n_vRM1hM5sipbora7xs5Y0qaOQrX_-6SF_j99_bWI_Og-y5OdoapprB_aXdkaOn8U816ac5ldp8r7g2bdU0oqwukwxUvyyubc-h6Zc3WIdtXcn7BXmtUVWMMwZ4uBqtr6rUq2YkocVSV0sf2yRZjy1Wdp8aFo0wRk2i0V55mOcvVVskQ07w9",
|
||||
"expires_in": 3599,
|
||||
"token_type": "Bearer"
|
||||
},
|
||||
"rawBytes": "eyJhY2Nlc3NfdG9rZW4iOiJ5YTI5LmMuYzBBU1JLMEdZcmU5R2VfNVYtRlJNS0UtRi0yT2wwNVZQdHppQVhGODh2aGFqbm9pQUc4Xzhib1hadmFVSnVvUUJjQnluUUl6ajdwckk1c3c3Y3RGMEpISWpGdWUyOGRXc25pZ00yeDVPOU40R3FRb05kc1VKUF9ibFJHN1VSNEtuMF9YcnV0Y3A3bEZ5cmZrT2R0Vk8yeU5FMzFuaUJFYmJYeDRhRTRlaGVNME1ISkJaNlV5aDFzQjV5d2Q1akl4NENzdTRxNHJEemI4SFlYZ3FqZUd2THJsc3AyRndNaFFGd1VkR2hnVWIxdGZVTE0wT3FaajdEV0I4aGozeTJ0Tk5KUmhCWXZvZ3lEaF9STmdaNi1Bb2xNM1B1eHRjam9mTG1WMHZZY2lsZVMwc3ByUEQ2eEVqWHpIMlNndld1NDM1VXBfdU96NVVEbVljbUgzZ3JjTndmdmpJTWNMUk5LSndoWHkySGx1SEpRZktUZThpUGEtN05uTTh4aEt4TEczODlDYjRyczFsNVJhZmJZUXAtaHRWV3lSVlctcTZKYWY3NlJuOEpZQnNKNXhkRlg3WXczMXNoWWJSbG84dzV6NGhleHJ3cGp3c29sV3N0NHdNb1UwdDRreU80c3h4ajBhQm5oWnprOHc3cE1CSjlTbDFrSWd2V3F3MnBGVVJZWFYzaFk0aGtWck9PSlVkVUoyZGVjSVZmMFgxV3NmNV9sdDJZN09lVzlidllYcVNZa2NZMTJfUzhRQi13NF8tT1YxNmhrX1diMkJKVV9hcWc4aVdrVk1wNDZJcUl1ZlEtMW9Tb3djY3dtTzFXV3NfVWc4d3I3Vm40OVVabVZGV0JRNDM2VWR6MHFVZWRhNzFTWTJyc25vVy1tMi1XeHhXSXdsNVltdmZqV3d2RkZaTWd6dmo4aTA4ajhjb2V5Ulp6X3A1N3NfVnFqazN2MGt6cm1PcjJNSk1VVllZbE9JSTVacThCRlUwOGRyQnpzejUwUS02ZjNTMU1CdDJkTzN2WWt6dzFNbDdqbnlrUW1VU3oxd1ZjZS1NOXplZmRhRlZlVnVjMXJqSXZpaXBWZU8zb2pGOTVGdmdoTU13blZ2VUY1NXBwekkzbl92Uk0xaE01c2lwYm9yYTd4czVZMHFhT1FyWF8tNlNGX2o5OV9iV0lfT2cteTVPZG9hcHByQl9hWGRrYU9uOFU4MTZhYzVsZHA4cjdnMmJkVTBvcXd1a3d4VXZ5eXViYy1oNlpjM1dJZHRYY243QlhtdFVWV01Nd1o0dUJxdHI2clVxMllrb2NWU1Ywc2YyeVJaankxV2RwOGFGbzB3UmsyaTBWNTVtT2N2VlZza1EwN3c5IiwiZXhwaXJlc19pbiI6MzU5OSwidG9rZW5fdHlwZSI6IkJlYXJlciJ9"
|
||||
}
|
||||
},
|
||||
"id": "1756994893546-oauth2.googleapis.com-POST-_token-58f6fdf2.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "GET",
|
||||
"path": "/v4/spreadsheets/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs",
|
||||
"queryStringParameters": {
|
||||
"fields": ["sheets.properties"]
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-l2-request-path": ["l2-managed-6"],
|
||||
"X-XSS-Protection": ["0"],
|
||||
"X-Frame-Options": ["SAMEORIGIN"],
|
||||
"X-Content-Type-Options": ["nosniff"],
|
||||
"Vary": ["Origin", "X-Origin", "Referer"],
|
||||
"Server": ["ESF"],
|
||||
"Date": ["Thu, 04 Sep 2025 14:07:57 GMT"],
|
||||
"Content-Type": ["application/json; charset=UTF-8"],
|
||||
"Alt-Svc": ["h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"]
|
||||
},
|
||||
"body": {
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"sheets": [
|
||||
{
|
||||
"properties": {
|
||||
"sheetId": 0,
|
||||
"title": "Sheet1",
|
||||
"index": 0,
|
||||
"sheetType": "GRID",
|
||||
"gridProperties": {
|
||||
"rowCount": 2001,
|
||||
"columnCount": 26
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"sheetId": 1911651598,
|
||||
"title": "Sheet2",
|
||||
"index": 1,
|
||||
"sheetType": "GRID",
|
||||
"gridProperties": {
|
||||
"rowCount": 1000,
|
||||
"columnCount": 26
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"rawBytes": "ewogICJzaGVldHMiOiBbCiAgICB7CiAgICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICJzaGVldElkIjogMCwKICAgICAgICAidGl0bGUiOiAiU2hlZXQxIiwKICAgICAgICAiaW5kZXgiOiAwLAogICAgICAgICJzaGVldFR5cGUiOiAiR1JJRCIsCiAgICAgICAgImdyaWRQcm9wZXJ0aWVzIjogewogICAgICAgICAgInJvd0NvdW50IjogMjAwMSwKICAgICAgICAgICJjb2x1bW5Db3VudCI6IDI2CiAgICAgICAgfQogICAgICB9CiAgICB9LAogICAgewogICAgICAicHJvcGVydGllcyI6IHsKICAgICAgICAic2hlZXRJZCI6IDE5MTE2NTE1OTgsCiAgICAgICAgInRpdGxlIjogIlNoZWV0MiIsCiAgICAgICAgImluZGV4IjogMSwKICAgICAgICAic2hlZXRUeXBlIjogIkdSSUQiLAogICAgICAgICJncmlkUHJvcGVydGllcyI6IHsKICAgICAgICAgICJyb3dDb3VudCI6IDEwMDAsCiAgICAgICAgICAiY29sdW1uQ291bnQiOiAyNgogICAgICAgIH0KICAgICAgfQogICAgfQogIF0KfQo="
|
||||
}
|
||||
},
|
||||
"id": "1756994893547-sheets.googleapis.com-GET-_v4_spreadsheets_1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs-185c9dd7.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "GET",
|
||||
"path": "/v4/spreadsheets/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/values/'Sheet2'",
|
||||
"queryStringParameters": {
|
||||
"valueRenderOption": ["UNFORMATTED_VALUE"],
|
||||
"dateTimeRenderOption": ["FORMATTED_STRING"]
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-l2-request-path": ["l2-managed-6"],
|
||||
"X-XSS-Protection": ["0"],
|
||||
"X-Frame-Options": ["SAMEORIGIN"],
|
||||
"X-Content-Type-Options": ["nosniff"],
|
||||
"Vary": ["Origin", "X-Origin", "Referer"],
|
||||
"Server": ["ESF"],
|
||||
"Date": ["Thu, 04 Sep 2025 14:07:59 GMT"],
|
||||
"Content-Type": ["application/json; charset=UTF-8"],
|
||||
"Alt-Svc": ["h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"]
|
||||
},
|
||||
"body": {
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"range": "Sheet2!A1:Z1000",
|
||||
"majorDimension": "ROWS",
|
||||
"values": [
|
||||
[
|
||||
"name",
|
||||
"email",
|
||||
"actual",
|
||||
"op",
|
||||
"output-row_number",
|
||||
"output-itemIndex",
|
||||
"output-runIndex",
|
||||
"data",
|
||||
"random-output"
|
||||
],
|
||||
[
|
||||
"test",
|
||||
"test",
|
||||
10,
|
||||
"output",
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
"output-0.17321991314554896",
|
||||
0.47763178373020865
|
||||
],
|
||||
["hello", "wolrd", 104, "", 3, 0, 0, "output-0.14637030644980253", 0.13015058088525477]
|
||||
]
|
||||
},
|
||||
"rawBytes": "ewogICJyYW5nZSI6ICJTaGVldDIhQTE6WjEwMDAiLAogICJtYWpvckRpbWVuc2lvbiI6ICJST1dTIiwKICAidmFsdWVzIjogWwogICAgWwogICAgICAibmFtZSIsCiAgICAgICJlbWFpbCIsCiAgICAgICJhY3R1YWwiLAogICAgICAib3AiLAogICAgICAib3V0cHV0LXJvd19udW1iZXIiLAogICAgICAib3V0cHV0LWl0ZW1JbmRleCIsCiAgICAgICJvdXRwdXQtcnVuSW5kZXgiLAogICAgICAiZGF0YSIsCiAgICAgICJyYW5kb20tb3V0cHV0IgogICAgXSwKICAgIFsKICAgICAgInRlc3QiLAogICAgICAidGVzdCIsCiAgICAgIDEwLAogICAgICAib3V0cHV0IiwKICAgICAgMiwKICAgICAgMCwKICAgICAgMCwKICAgICAgIm91dHB1dC0wLjE3MzIxOTkxMzE0NTU0ODk2IiwKICAgICAgMC40Nzc2MzE3ODM3MzAyMDg2NQogICAgXSwKICAgIFsKICAgICAgImhlbGxvIiwKICAgICAgIndvbHJkIiwKICAgICAgMTA0LAogICAgICAiIiwKICAgICAgMywKICAgICAgMCwKICAgICAgMCwKICAgICAgIm91dHB1dC0wLjE0NjM3MDMwNjQ0OTgwMjUzIiwKICAgICAgMC4xMzAxNTA1ODA4ODUyNTQ3NwogICAgXQogIF0KfQo="
|
||||
}
|
||||
},
|
||||
"id": "1756994893548-sheets.googleapis.com-GET-_v4_spreadsheets_1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs_values__Sheet2_-7746917a.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "GET",
|
||||
"path": "/v4/spreadsheets/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/values/Sheet2!2:1000",
|
||||
"queryStringParameters": {
|
||||
"valueRenderOption": ["UNFORMATTED_VALUE"],
|
||||
"dateTimeRenderOption": ["FORMATTED_STRING"]
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-l2-request-path": ["l2-managed-6"],
|
||||
"X-XSS-Protection": ["0"],
|
||||
"X-Frame-Options": ["SAMEORIGIN"],
|
||||
"X-Content-Type-Options": ["nosniff"],
|
||||
"Vary": ["Origin", "X-Origin", "Referer"],
|
||||
"Server": ["ESF"],
|
||||
"Date": ["Thu, 04 Sep 2025 14:08:00 GMT"],
|
||||
"Content-Type": ["application/json; charset=UTF-8"],
|
||||
"Alt-Svc": ["h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"]
|
||||
},
|
||||
"body": {
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"range": "Sheet2!A2:Z1000",
|
||||
"majorDimension": "ROWS",
|
||||
"values": [
|
||||
[
|
||||
"test",
|
||||
"test",
|
||||
10,
|
||||
"output",
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
"output-0.17321991314554896",
|
||||
0.47763178373020865
|
||||
],
|
||||
["hello", "wolrd", 104, "", 3, 0, 0, "output-0.14637030644980253", 0.13015058088525477]
|
||||
]
|
||||
},
|
||||
"rawBytes": "ewogICJyYW5nZSI6ICJTaGVldDIhQTI6WjEwMDAiLAogICJtYWpvckRpbWVuc2lvbiI6ICJST1dTIiwKICAidmFsdWVzIjogWwogICAgWwogICAgICAidGVzdCIsCiAgICAgICJ0ZXN0IiwKICAgICAgMTAsCiAgICAgICJvdXRwdXQiLAogICAgICAyLAogICAgICAwLAogICAgICAwLAogICAgICAib3V0cHV0LTAuMTczMjE5OTEzMTQ1NTQ4OTYiLAogICAgICAwLjQ3NzYzMTc4MzczMDIwODY1CiAgICBdLAogICAgWwogICAgICAiaGVsbG8iLAogICAgICAid29scmQiLAogICAgICAxMDQsCiAgICAgICIiLAogICAgICAzLAogICAgICAwLAogICAgICAwLAogICAgICAib3V0cHV0LTAuMTQ2MzcwMzA2NDQ5ODAyNTMiLAogICAgICAwLjEzMDE1MDU4MDg4NTI1NDc3CiAgICBdCiAgXQp9Cg=="
|
||||
}
|
||||
},
|
||||
"id": "1756994893549-sheets.googleapis.com-GET-_v4_spreadsheets_1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs_values_Sheet2_2_1000-e7fa67bd.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "POST",
|
||||
"path": "/v4/spreadsheets/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/values:batchUpdate",
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"data": [
|
||||
{
|
||||
"range": "Sheet2!C2",
|
||||
"values": [[11]]
|
||||
}
|
||||
],
|
||||
"valueInputOption": "RAW"
|
||||
},
|
||||
"rawBytes": "eyJkYXRhIjpbeyJyYW5nZSI6IlNoZWV0MiFDMiIsInZhbHVlcyI6W1sxMV1dfV0sInZhbHVlSW5wdXRPcHRpb24iOiJSQVcifQ=="
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-l2-request-path": ["l2-managed-6"],
|
||||
"X-XSS-Protection": ["0"],
|
||||
"X-Frame-Options": ["SAMEORIGIN"],
|
||||
"X-Content-Type-Options": ["nosniff"],
|
||||
"Vary": ["Origin", "X-Origin", "Referer"],
|
||||
"Server": ["ESF"],
|
||||
"Date": ["Thu, 04 Sep 2025 14:08:04 GMT"],
|
||||
"Content-Type": ["application/json; charset=UTF-8"],
|
||||
"Alt-Svc": ["h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"]
|
||||
},
|
||||
"body": {
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"spreadsheetId": "1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs",
|
||||
"totalUpdatedRows": 1,
|
||||
"totalUpdatedColumns": 1,
|
||||
"totalUpdatedCells": 1,
|
||||
"totalUpdatedSheets": 1,
|
||||
"responses": [
|
||||
{
|
||||
"spreadsheetId": "1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs",
|
||||
"updatedRange": "Sheet2!C2",
|
||||
"updatedRows": 1,
|
||||
"updatedColumns": 1,
|
||||
"updatedCells": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"rawBytes": "ewogICJzcHJlYWRzaGVldElkIjogIjF6SnU4ekx0RmMzcmJaUEFXcUtCaDdUSW91NTlTdEpyN0hMbjBuVXkwYnFzIiwKICAidG90YWxVcGRhdGVkUm93cyI6IDEsCiAgInRvdGFsVXBkYXRlZENvbHVtbnMiOiAxLAogICJ0b3RhbFVwZGF0ZWRDZWxscyI6IDEsCiAgInRvdGFsVXBkYXRlZFNoZWV0cyI6IDEsCiAgInJlc3BvbnNlcyI6IFsKICAgIHsKICAgICAgInNwcmVhZHNoZWV0SWQiOiAiMXpKdTh6THRGYzNyYlpQQVdxS0JoN1RJb3U1OVN0SnI3SExuMG5VeTBicXMiLAogICAgICAidXBkYXRlZFJhbmdlIjogIlNoZWV0MiFDMiIsCiAgICAgICJ1cGRhdGVkUm93cyI6IDEsCiAgICAgICJ1cGRhdGVkQ29sdW1ucyI6IDEsCiAgICAgICJ1cGRhdGVkQ2VsbHMiOiAxCiAgICB9CiAgXQp9Cg=="
|
||||
}
|
||||
},
|
||||
"id": "1756994893550-sheets.googleapis.com-POST-_v4_spreadsheets_1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs_values_batchUpdate-19f43fca.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "PUT",
|
||||
"path": "/v4/spreadsheets/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/values/Sheet2!1:1",
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"range": "Sheet2!1:1",
|
||||
"values": [
|
||||
[
|
||||
"name",
|
||||
"email",
|
||||
"actual",
|
||||
"op",
|
||||
"output-row_number",
|
||||
"output-itemIndex",
|
||||
"output-runIndex",
|
||||
"data",
|
||||
"random-output"
|
||||
]
|
||||
]
|
||||
},
|
||||
"rawBytes": "eyJyYW5nZSI6IlNoZWV0MiExOjEiLCJ2YWx1ZXMiOltbIm5hbWUiLCJlbWFpbCIsImFjdHVhbCIsIm9wIiwib3V0cHV0LXJvd19udW1iZXIiLCJvdXRwdXQtaXRlbUluZGV4Iiwib3V0cHV0LXJ1bkluZGV4IiwiZGF0YSIsInJhbmRvbS1vdXRwdXQiXV19"
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-l2-request-path": ["l2-managed-6"],
|
||||
"X-XSS-Protection": ["0"],
|
||||
"X-Frame-Options": ["SAMEORIGIN"],
|
||||
"X-Content-Type-Options": ["nosniff"],
|
||||
"Vary": ["Origin", "X-Origin", "Referer"],
|
||||
"Server": ["ESF"],
|
||||
"Date": ["Thu, 04 Sep 2025 14:08:02 GMT"],
|
||||
"Content-Type": ["application/json; charset=UTF-8"],
|
||||
"Alt-Svc": ["h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"]
|
||||
},
|
||||
"body": {
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"spreadsheetId": "1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs",
|
||||
"updatedRange": "Sheet2!A1:I1",
|
||||
"updatedRows": 1,
|
||||
"updatedColumns": 9,
|
||||
"updatedCells": 9
|
||||
},
|
||||
"rawBytes": "ewogICJzcHJlYWRzaGVldElkIjogIjF6SnU4ekx0RmMzcmJaUEFXcUtCaDdUSW91NTlTdEpyN0hMbjBuVXkwYnFzIiwKICAidXBkYXRlZFJhbmdlIjogIlNoZWV0MiFBMTpJMSIsCiAgInVwZGF0ZWRSb3dzIjogMSwKICAidXBkYXRlZENvbHVtbnMiOiA5LAogICJ1cGRhdGVkQ2VsbHMiOiA5Cn0K"
|
||||
}
|
||||
},
|
||||
"id": "1756994893550-sheets.googleapis.com-PUT-_v4_spreadsheets_1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs_values_Sheet2_1_1-c0a137d1.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "GET",
|
||||
"path": "/v4/spreadsheets/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/values/Sheet2!3:1000",
|
||||
"queryStringParameters": {
|
||||
"valueRenderOption": ["UNFORMATTED_VALUE"],
|
||||
"dateTimeRenderOption": ["FORMATTED_STRING"]
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-l2-request-path": ["l2-managed-6"],
|
||||
"X-XSS-Protection": ["0"],
|
||||
"X-Frame-Options": ["SAMEORIGIN"],
|
||||
"X-Content-Type-Options": ["nosniff"],
|
||||
"Vary": ["Origin", "X-Origin", "Referer"],
|
||||
"Server": ["ESF"],
|
||||
"Date": ["Thu, 04 Sep 2025 14:08:07 GMT"],
|
||||
"Content-Type": ["application/json; charset=UTF-8"],
|
||||
"Alt-Svc": ["h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"]
|
||||
},
|
||||
"body": {
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"range": "Sheet2!A3:Z1000",
|
||||
"majorDimension": "ROWS",
|
||||
"values": [
|
||||
["hello", "wolrd", 104, "", 3, 0, 0, "output-0.14637030644980253", 0.13015058088525477]
|
||||
]
|
||||
},
|
||||
"rawBytes": "ewogICJyYW5nZSI6ICJTaGVldDIhQTM6WjEwMDAiLAogICJtYWpvckRpbWVuc2lvbiI6ICJST1dTIiwKICAidmFsdWVzIjogWwogICAgWwogICAgICAiaGVsbG8iLAogICAgICAid29scmQiLAogICAgICAxMDQsCiAgICAgICIiLAogICAgICAzLAogICAgICAwLAogICAgICAwLAogICAgICAib3V0cHV0LTAuMTQ2MzcwMzA2NDQ5ODAyNTMiLAogICAgICAwLjEzMDE1MDU4MDg4NTI1NDc3CiAgICBdCiAgXQp9Cg=="
|
||||
}
|
||||
},
|
||||
"id": "1756994893551-sheets.googleapis.com-GET-_v4_spreadsheets_1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs_values_Sheet2_3_1000-1bdb2093.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "POST",
|
||||
"path": "/v4/spreadsheets/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/values:batchUpdate",
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"data": [
|
||||
{
|
||||
"range": "Sheet2!C3",
|
||||
"values": [[105]]
|
||||
}
|
||||
],
|
||||
"valueInputOption": "RAW"
|
||||
},
|
||||
"rawBytes": "eyJkYXRhIjpbeyJyYW5nZSI6IlNoZWV0MiFDMyIsInZhbHVlcyI6W1sxMDVdXX1dLCJ2YWx1ZUlucHV0T3B0aW9uIjoiUkFXIn0="
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-l2-request-path": ["l2-managed-6"],
|
||||
"X-XSS-Protection": ["0"],
|
||||
"X-Frame-Options": ["SAMEORIGIN"],
|
||||
"X-Content-Type-Options": ["nosniff"],
|
||||
"Vary": ["Origin", "X-Origin", "Referer"],
|
||||
"Server": ["ESF"],
|
||||
"Date": ["Thu, 04 Sep 2025 14:08:11 GMT"],
|
||||
"Content-Type": ["application/json; charset=UTF-8"],
|
||||
"Alt-Svc": ["h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"]
|
||||
},
|
||||
"body": {
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"spreadsheetId": "1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs",
|
||||
"totalUpdatedRows": 1,
|
||||
"totalUpdatedColumns": 1,
|
||||
"totalUpdatedCells": 1,
|
||||
"totalUpdatedSheets": 1,
|
||||
"responses": [
|
||||
{
|
||||
"spreadsheetId": "1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs",
|
||||
"updatedRange": "Sheet2!C3",
|
||||
"updatedRows": 1,
|
||||
"updatedColumns": 1,
|
||||
"updatedCells": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"rawBytes": "ewogICJzcHJlYWRzaGVldElkIjogIjF6SnU4ekx0RmMzcmJaUEFXcUtCaDdUSW91NTlTdEpyN0hMbjBuVXkwYnFzIiwKICAidG90YWxVcGRhdGVkUm93cyI6IDEsCiAgInRvdGFsVXBkYXRlZENvbHVtbnMiOiAxLAogICJ0b3RhbFVwZGF0ZWRDZWxscyI6IDEsCiAgInRvdGFsVXBkYXRlZFNoZWV0cyI6IDEsCiAgInJlc3BvbnNlcyI6IFsKICAgIHsKICAgICAgInNwcmVhZHNoZWV0SWQiOiAiMXpKdTh6THRGYzNyYlpQQVdxS0JoN1RJb3U1OVN0SnI3SExuMG5VeTBicXMiLAogICAgICAidXBkYXRlZFJhbmdlIjogIlNoZWV0MiFDMyIsCiAgICAgICJ1cGRhdGVkUm93cyI6IDEsCiAgICAgICJ1cGRhdGVkQ29sdW1ucyI6IDEsCiAgICAgICJ1cGRhdGVkQ2VsbHMiOiAxCiAgICB9CiAgXQp9Cg=="
|
||||
}
|
||||
},
|
||||
"id": "1756994893552-sheets.googleapis.com-POST-_v4_spreadsheets_1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs_values_batchUpdate-65f181a6.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -182,7 +182,6 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
|
||||
|
||||
const serverUrl = `http://${proxyServerContainer?.getHost()}:${proxyServerContainer?.getFirstMappedPort()}`;
|
||||
const proxyServer = new ProxyServer(serverUrl);
|
||||
await proxyServer.loadExpectations();
|
||||
|
||||
await use(proxyServer);
|
||||
},
|
||||
|
||||
@@ -4,11 +4,22 @@
|
||||
|
||||
import crypto from 'crypto';
|
||||
import { promises as fs } from 'fs';
|
||||
import type { Expectation, HttpRequest } from 'mockserver-client';
|
||||
import type { Expectation, RequestDefinition } from 'mockserver-client';
|
||||
import { mockServerClient as proxyServerClient } from 'mockserver-client';
|
||||
import type { MockServerClient, RequestResponse } from 'mockserver-client/mockServerClient';
|
||||
import type { HttpRequest, HttpResponse } from 'mockserver-client/mockServer';
|
||||
import type {
|
||||
MockServerClient,
|
||||
PathOrRequestDefinition,
|
||||
RequestResponse,
|
||||
} from 'mockserver-client/mockServerClient';
|
||||
import { join } from 'path';
|
||||
|
||||
export type RequestMade = {
|
||||
httpRequest?: HttpRequest;
|
||||
httpResponse?: HttpResponse;
|
||||
timestamp?: string;
|
||||
};
|
||||
|
||||
export interface ProxyServerRequest {
|
||||
method: string;
|
||||
path: string;
|
||||
@@ -60,17 +71,18 @@ export class ProxyServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all expectations from the expectations directory and mock them
|
||||
* Load all expectations from the specified subfolder and mock them
|
||||
*/
|
||||
async loadExpectations(): Promise<void> {
|
||||
async loadExpectations(folderName: string): Promise<void> {
|
||||
try {
|
||||
const files = await fs.readdir(this.expectationsDir);
|
||||
const targetDir = join(this.expectationsDir, folderName);
|
||||
const files = await fs.readdir(targetDir);
|
||||
const jsonFiles = files.filter((file) => file.endsWith('.json'));
|
||||
const expectations: Expectation[] = [];
|
||||
|
||||
for (const file of jsonFiles) {
|
||||
try {
|
||||
const filePath = join(this.expectationsDir, file);
|
||||
const filePath = join(targetDir, file);
|
||||
const fileContent = await fs.readFile(filePath, 'utf8');
|
||||
const expectation = JSON.parse(fileContent);
|
||||
expectations.push(expectation);
|
||||
@@ -108,11 +120,12 @@ export class ProxyServer {
|
||||
/**
|
||||
* Verify that a request was received by ProxyServer
|
||||
*/
|
||||
async verifyRequest(request: ProxyServerRequest, numberOfRequests: number): Promise<boolean> {
|
||||
async verifyRequest(request: RequestDefinition, numberOfRequests: number): Promise<boolean> {
|
||||
try {
|
||||
await this.client.verify(request, numberOfRequests);
|
||||
await this.client.verify(request, numberOfRequests, numberOfRequests);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -120,16 +133,16 @@ export class ProxyServer {
|
||||
/**
|
||||
* Clear all expectations and logs from ProxyServer
|
||||
*/
|
||||
async clearProxyServer(): Promise<void> {
|
||||
async clearAllExpectations(): Promise<void> {
|
||||
try {
|
||||
await this.client.clear(null, 'ALL');
|
||||
await this.client.clear('', 'ALL');
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to clear ProxyServer: ${JSON.stringify(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a simple GET request expectation with JSON response
|
||||
* Create a request expectation with JSON response
|
||||
*/
|
||||
async createGetExpectation(
|
||||
path: string,
|
||||
@@ -161,40 +174,50 @@ export class ProxyServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a GET request was made to ProxyServer
|
||||
* Verify a request was made to ProxyServer
|
||||
*/
|
||||
async wasGetRequestMade(
|
||||
path: string,
|
||||
queryParams?: Record<string, string>,
|
||||
numberOfRequests = 1,
|
||||
): Promise<boolean> {
|
||||
const queryStringParameters = queryParams
|
||||
? Object.entries(queryParams).reduce<Record<string, string[]>>((acc, [key, value]) => {
|
||||
acc[key] = [value];
|
||||
return acc;
|
||||
}, {})
|
||||
: undefined;
|
||||
async wasRequestMade(request: RequestDefinition, numberOfRequests = 1): Promise<boolean> {
|
||||
return await this.verifyRequest(request, numberOfRequests);
|
||||
}
|
||||
|
||||
return await this.verifyRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path,
|
||||
...(queryStringParameters && { queryStringParameters }),
|
||||
},
|
||||
numberOfRequests,
|
||||
);
|
||||
async getAllRequestsMade(): Promise<RequestMade[]> {
|
||||
// @ts-expect-error mockserver types seem to be messed up
|
||||
return await this.client.retrieveRecordedRequestsAndResponses('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve recorded expectations and write to files
|
||||
*
|
||||
* @param folderName - Target folder name for saving expectation files
|
||||
* @param options - Optional configuration
|
||||
* @param options.pathOrRequestDefinition - Filter expectations by path or request definition
|
||||
* @param options.host - Filter expectations by host name (partial match)
|
||||
* @param options.dedupe - Remove duplicate expectations based on request
|
||||
* @param options.raw - Save full original requests (true) or cleaned requests (false, default)
|
||||
* - raw: false (default) - Saves only essential fields: method, path, queryStringParameters (GET), body (POST/PUT)
|
||||
* - raw: true - Saves complete original request including all headers and metadata
|
||||
*/
|
||||
async recordExpectations(request?: HttpRequest): Promise<void> {
|
||||
async recordExpectations(
|
||||
folderName: string,
|
||||
options?: {
|
||||
pathOrRequestDefinition?: PathOrRequestDefinition;
|
||||
host?: string;
|
||||
dedupe?: boolean;
|
||||
raw?: boolean;
|
||||
},
|
||||
): Promise<void> {
|
||||
try {
|
||||
// Retrieve recorded expectations from the mock server
|
||||
const recordedExpectations = await this.client.retrieveRecordedExpectations(request);
|
||||
const recordedExpectations = await this.client.retrieveRecordedExpectations(
|
||||
options?.pathOrRequestDefinition,
|
||||
);
|
||||
|
||||
// Ensure expectations directory exists
|
||||
await fs.mkdir(this.expectationsDir, { recursive: true });
|
||||
// Create target directory path
|
||||
const targetDir = join(this.expectationsDir, folderName);
|
||||
|
||||
// Ensure target directory exists
|
||||
await fs.mkdir(targetDir, { recursive: true });
|
||||
const seenRequests = new Set<string>();
|
||||
|
||||
for (const expectation of recordedExpectations) {
|
||||
if (
|
||||
@@ -208,25 +231,77 @@ export class ProxyServer {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate unique filename based on request details
|
||||
const requestData = {
|
||||
method: expectation.httpRequest?.method,
|
||||
path: expectation.httpRequest?.path,
|
||||
queryStringParameters: expectation.httpRequest?.queryStringParameters,
|
||||
headers: expectation.httpRequest?.headers,
|
||||
// Extract host for filename and filtering
|
||||
const headers = expectation.httpRequest.headers ?? {};
|
||||
const hostHeader = 'Host' in headers ? headers?.Host : undefined;
|
||||
const hostName = Array.isArray(hostHeader) ? hostHeader[0] : (hostHeader ?? 'unknown-host');
|
||||
|
||||
if (options?.host && typeof hostName === 'string' && !hostName.includes(options.host)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const method = expectation.httpRequest.method;
|
||||
let requestForProcessing: Record<string, unknown> | HttpRequest;
|
||||
|
||||
if (options?.raw) {
|
||||
// Use raw request without cleaning
|
||||
requestForProcessing = expectation.httpRequest;
|
||||
} else {
|
||||
// Clean up the request data
|
||||
const cleanedRequest: Record<string, unknown> = {
|
||||
method: expectation.httpRequest.method,
|
||||
path: expectation.httpRequest.path,
|
||||
};
|
||||
|
||||
// Include different fields based on method
|
||||
if (method === 'GET') {
|
||||
// For GET requests, include queryStringParameters if present
|
||||
if (expectation.httpRequest.queryStringParameters) {
|
||||
cleanedRequest.queryStringParameters = expectation.httpRequest.queryStringParameters;
|
||||
}
|
||||
} else if (method === 'POST' || method === 'PUT') {
|
||||
// For POST/PUT requests, include body if present
|
||||
if (expectation.httpRequest.body) {
|
||||
cleanedRequest.body = expectation.httpRequest.body;
|
||||
}
|
||||
}
|
||||
|
||||
requestForProcessing = cleanedRequest;
|
||||
}
|
||||
|
||||
// Dedupe expectations if requested
|
||||
if (options?.dedupe) {
|
||||
const dedupeKey = JSON.stringify(requestForProcessing);
|
||||
|
||||
if (seenRequests.has(dedupeKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
seenRequests.add(dedupeKey);
|
||||
}
|
||||
|
||||
// Create expectation (cleaned or raw)
|
||||
const processedExpectation: Expectation = {
|
||||
...expectation,
|
||||
httpRequest: requestForProcessing,
|
||||
times: {
|
||||
unlimited: true,
|
||||
},
|
||||
};
|
||||
|
||||
// Generate unique filename based on request details
|
||||
const hash = crypto
|
||||
.createHash('sha256')
|
||||
.update(JSON.stringify(requestData))
|
||||
.update(JSON.stringify(requestForProcessing))
|
||||
.digest('hex')
|
||||
.substring(0, 8);
|
||||
|
||||
const filename = `${expectation.httpRequest?.method?.toString()}-${expectation.httpRequest?.path?.replace(/[^a-zA-Z0-9]/g, '_')}-${hash}.json`;
|
||||
const filePath = join(this.expectationsDir, filename);
|
||||
const filename = `${Date.now()}-${hostName}-${method}-${expectation.httpRequest.path.replace(/[^a-zA-Z0-9]/g, '_')}-${hash}.json`;
|
||||
processedExpectation.id = filename;
|
||||
const filePath = join(targetDir, filename);
|
||||
|
||||
// Write expectation to JSON file
|
||||
await fs.writeFile(filePath, JSON.stringify(expectation, null, 2));
|
||||
await fs.writeFile(filePath, JSON.stringify(processedExpectation, null, 2));
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to record expectations: ${JSON.stringify(error)}`);
|
||||
|
||||
@@ -4,6 +4,10 @@ import { test, expect } from '../../fixtures/base';
|
||||
|
||||
// @capability:proxy tag ensures that test suite is only run when proxy is available
|
||||
test.describe('Proxy server @capability:proxy', () => {
|
||||
test.beforeEach(async ({ proxyServer }) => {
|
||||
await proxyServer.clearAllExpectations();
|
||||
});
|
||||
|
||||
test('should verify ProxyServer container is running', async ({ proxyServer }) => {
|
||||
const mockResponse = await proxyServer.createGetExpectation('/health', {
|
||||
status: 'healthy',
|
||||
@@ -12,7 +16,7 @@ test.describe('Proxy server @capability:proxy', () => {
|
||||
assert(typeof mockResponse !== 'string');
|
||||
expect(mockResponse.statusCode).toBe(201);
|
||||
|
||||
expect(await proxyServer.wasGetRequestMade('/health')).toBe(false);
|
||||
expect(await proxyServer.wasRequestMade({ method: 'GET', path: '/health' })).toBe(false);
|
||||
|
||||
// Verify the mock endpoint works
|
||||
const healthResponse = await fetch(`${proxyServer.url}/health`);
|
||||
@@ -20,7 +24,7 @@ test.describe('Proxy server @capability:proxy', () => {
|
||||
const healthData = await healthResponse.json();
|
||||
expect(healthData.status).toBe('healthy');
|
||||
|
||||
expect(await proxyServer.wasGetRequestMade('/health')).toBe(true);
|
||||
expect(await proxyServer.wasRequestMade({ method: 'GET', path: '/health' })).toBe(true);
|
||||
});
|
||||
|
||||
test('should run a simple workflow calling http endpoint', async ({ n8n, proxyServer }) => {
|
||||
@@ -41,18 +45,22 @@ test.describe('Proxy server @capability:proxy', () => {
|
||||
|
||||
// Verify the request was handled by mockserver
|
||||
expect(
|
||||
await proxyServer.wasGetRequestMade('/data', {
|
||||
test: '1',
|
||||
await proxyServer.wasRequestMade({
|
||||
method: 'GET',
|
||||
path: '/data',
|
||||
queryStringParameters: { test: ['1'] },
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('should use stored expectations respond to api request', async ({ proxyServer }) => {
|
||||
await proxyServer.loadExpectations('proxy-server');
|
||||
|
||||
const response = await fetch(`${proxyServer.url}/mock-endpoint`);
|
||||
expect(response.ok).toBe(true);
|
||||
const data = await response.json();
|
||||
expect(data.title).toBe('delectus aut autem');
|
||||
expect(await proxyServer.wasGetRequestMade('/mock-endpoint')).toBe(true);
|
||||
expect(await proxyServer.wasRequestMade({ method: 'GET', path: '/mock-endpoint' })).toBe(true);
|
||||
});
|
||||
|
||||
test('should run a simple workflow proxying HTTPS request', async ({ n8n }) => {
|
||||
|
||||
112
packages/testing/playwright/tests/ui/evaluations.spec.ts
Normal file
112
packages/testing/playwright/tests/ui/evaluations.spec.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { expect, test } from '../../fixtures/base';
|
||||
|
||||
test.describe('Evaluations @capability:proxy', () => {
|
||||
test.beforeEach(async ({ n8n, proxyServer }) => {
|
||||
await proxyServer.clearAllExpectations();
|
||||
|
||||
await n8n.goHome();
|
||||
await n8n.workflows.clickAddWorkflowButton();
|
||||
});
|
||||
|
||||
test('should load evaluations workflow and execute twice', async ({ n8n, api, proxyServer }) => {
|
||||
await proxyServer.loadExpectations('evaluations');
|
||||
|
||||
await api.credentialApi.createCredentialFromDefinition({
|
||||
name: 'Test Google Sheets',
|
||||
type: 'googleApi',
|
||||
data: {
|
||||
email: 'email@quickstart-1234.iam.gserviceaccount.com',
|
||||
// mock private key
|
||||
privateKey: `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDx1//AaoSkyHYl
|
||||
npqS3+uaePYhJXKD/T1h6zGThAUooN7ZzWK46nNcU1vghQMTlPMHfUTbl4xzZxEL
|
||||
OYjyTPOKpwJvhmy44MU+zTQYJuUaU4dQuOCnnC61CL91Xy+8GJd7PvdUeVRWENWu
|
||||
zzO825Fxeiy2qnbrOJfhYh+f9znwWM2R8/V6LIp1HSWNBU0h/NCesmVhGwTP2H/P
|
||||
wGgFPzl9+effW8TgmAukVuZoG+z8pOiqJnZLgTOO++PLyM6UJe560UnAbv0yP4y5
|
||||
lZ370XwOQ6gVIiB0+8Z2A3tJp6ackfoMfDYbuU+CAhFPqkdvXgbrYciUCr6fzINo
|
||||
ImK6CcSDAgMBAAECggEAF0+XokdiI7QC11tzUMbuocQZDVbbs+c7/G08KRjnmmPv
|
||||
NxU599L5baPHTlvj0QZhao5jjbsM2a7MkMVp8tkB/JJehLtzTVq1CHmlFNLi8Geu
|
||||
ulQnq2A9jEuckMatBjdkmoeWNXlAbM9QmXn1ZbXQThzVpIHH1qJs2Veo7rVYy1bD
|
||||
+hnzadyeXsHOC518wNAaF3b1UShybI3dlrHbXqqRmkOZP272IKfmvZ2KOcnFC+MT
|
||||
cWLUGWBTq2YK+UJv09OXHEBnonrm18m2Sku+/PhFwjOiifIK/1MWILss60IB7dFm
|
||||
7Fe7NAtYQMPZyDEqY5Xo+K4FwWYzfxfHPiJf7k0DqQKBgQD5Rz+HCZC8V5c1oK8/
|
||||
1hGthyh5JdXxW7C8D1WVuo7W2OHrOJSDXjGhsxMjnKYdq/1YybJl9XpQSvZeumto
|
||||
YazNiJqAexIlpmEHLW5gDtX3xpM0dujuJudTHYfveugtR8i/EZpWpFKv45/6Rm33
|
||||
Yt2PaMjLuO7yW0buEjSQInHtHwKBgQD4XW44YujgF+xvMmx8+QyyNI2UNI1ZmnsU
|
||||
VZLmDAn5+WDz5YtBXN9JGIXIk5279S7xzu9xyq7Ih6uedxE/hmzaHSZ1gl9Xasci
|
||||
n86FGaGPm6RtEeZ8c68oqha7kddLoBwTPBoZq5NaCCaTh2TQkMPg+Ws3erM0pkyC
|
||||
fqw1hzkYHQKBgQC2Iv3i3/VV+DXupCqIXRRrkx7abe/FO3aF4jppfXdSugNQR/YT
|
||||
imZ/PIXWdmXVtk4VasIjx1oIgs1C57kE+qE1SAODrujSg5/Pi71jCFQEh54VLnEB
|
||||
WYGZ9DDXpRkxxIqEOQtpFQWpqIrCZmWA5Ub3uttEJyrIADNyTfEEA3b0hwKBgHrn
|
||||
STbQA2t5iz/PlQ4W9GhvRyxzAQu5PXTnj+UVSg6QkKDBE7NJsRjr8LA8FE9B2nRA
|
||||
sg7+fJWxRYUKaNelvtIEoNZ/qIyKw3Zn3HvTHjcBj1GGDSfC24fk+5Dgb8j1t07x
|
||||
a/0OAcIIzIYu9v2a1cPLyXnP10STksL0ymVGwEMlAoGBAK2dtYZllhooN/C4ssFW
|
||||
nmfqICLWEc/UZSxmxau1rOz71GJiiHgXFmQgiZtpf3Qp3wKKtoFkf+sJ6zP2VX35
|
||||
2tJcTO9lKm6kNa3eaveE/NJrkH5a0IpxrvDT1TvmnapaNEKuGZJAX5BNaggDrfEJ
|
||||
m82JpEptTfAxFHtd8+Sb0U2G
|
||||
-----END PRIVATE KEY-----`,
|
||||
},
|
||||
});
|
||||
|
||||
// Import the evaluations workflow
|
||||
await n8n.canvas.importWorkflow('evaluations_loop.json', 'Evaluations');
|
||||
|
||||
// Open each node to ensure credentials are set
|
||||
await n8n.canvas.openNode('When fetching a dataset row');
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
|
||||
// Open each node to ensure credentials are set
|
||||
await n8n.canvas.openNode('Set outputs');
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
|
||||
// Execute workflow from canvas - first execution
|
||||
await n8n.canvas.clickExecuteWorkflowButton();
|
||||
|
||||
// wait for first run to finish
|
||||
await n8n.notifications.waitForNotificationAndClose('Successful', { timeout: 10000 });
|
||||
|
||||
// wait for second run to finish
|
||||
await n8n.notifications.waitForNotificationAndClose('Successful', { timeout: 10000 });
|
||||
|
||||
// 💡 To update recordings, remove stored expectations, set real credentials above and rerecord here.
|
||||
// await proxyServer.recordExpectations('evaluations', { host: 'google', dedupe: true });
|
||||
|
||||
const batchUpdateRequests = (await proxyServer.getAllRequestsMade()).filter((request) => {
|
||||
const path = request.httpRequest?.path;
|
||||
const method = request.httpRequest?.method;
|
||||
|
||||
return method === 'POST' && typeof path === 'string' && path.endsWith('/values:batchUpdate');
|
||||
});
|
||||
|
||||
/**
|
||||
* Original Table in Google Sheets
|
||||
* The loop should execute twice over both rows here
|
||||
* Incrementing each value by 1 (expression in Set Output node)
|
||||
*
|
||||
* name email actual
|
||||
test test 10
|
||||
hello wolrd 104
|
||||
*/
|
||||
|
||||
// Set output node was called twice in a loop, updating Google sheets output value
|
||||
expect(batchUpdateRequests.length).toEqual(2);
|
||||
expect((batchUpdateRequests[0]?.httpRequest?.body as { json: object })?.json).toEqual({
|
||||
data: [
|
||||
{
|
||||
range: 'Sheet2!C2',
|
||||
values: [[11]],
|
||||
},
|
||||
],
|
||||
valueInputOption: 'RAW',
|
||||
});
|
||||
expect((batchUpdateRequests[1]?.httpRequest?.body as { json: object })?.json).toEqual({
|
||||
data: [
|
||||
{
|
||||
range: 'Sheet2!C3',
|
||||
values: [[105]],
|
||||
},
|
||||
],
|
||||
valueInputOption: 'RAW',
|
||||
});
|
||||
});
|
||||
});
|
||||
160
packages/testing/playwright/workflows/evaluations_loop.json
Normal file
160
packages/testing/playwright/workflows/evaluations_loop.json
Normal file
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"value": "1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs",
|
||||
"mode": "list",
|
||||
"cachedResultName": "Evaluation test - mutasem",
|
||||
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/edit?usp=drivesdk"
|
||||
},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"value": 1911651598,
|
||||
"mode": "list",
|
||||
"cachedResultName": "Sheet2",
|
||||
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/edit#gid=1911651598"
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.evaluationTrigger",
|
||||
"typeVersion": 4.6,
|
||||
"position": [0, 0],
|
||||
"id": "2353d300-628d-4e9f-86ad-89b3b78bf02f",
|
||||
"name": "When fetching a dataset row",
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "DQuAchCa7lPMNsOG",
|
||||
"name": "Google Sheets account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"value": "1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs",
|
||||
"mode": "list",
|
||||
"cachedResultName": "Evaluation test - mutasem",
|
||||
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/edit?usp=drivesdk"
|
||||
},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"value": 1911651598,
|
||||
"mode": "list",
|
||||
"cachedResultName": "Sheet2",
|
||||
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1zJu8zLtFc3rbZPAWqKBh7TIou59StJr7HLn0nUy0bqs/edit#gid=1911651598"
|
||||
},
|
||||
"outputs": {
|
||||
"values": [
|
||||
{
|
||||
"outputName": "actual",
|
||||
"outputValue": "={{ parseInt($json.actual) + 1 }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.evaluation",
|
||||
"typeVersion": 4.7,
|
||||
"position": [640, 0],
|
||||
"id": "47bf60d4-1c60-4dcc-a9d7-af0c02c84db3",
|
||||
"name": "Set outputs",
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "DQuAchCa7lPMNsOG",
|
||||
"name": "Google Sheets account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"amount": 1
|
||||
},
|
||||
"type": "n8n-nodes-base.wait",
|
||||
"typeVersion": 1.1,
|
||||
"position": [208, 0],
|
||||
"id": "2b5f3437-19bc-4d2e-ac60-52bfbb0fec1b",
|
||||
"name": "Wait",
|
||||
"webhookId": "26826666-e8e8-492e-9619-49e604fc90ee"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "setInputs",
|
||||
"inputs": {
|
||||
"values": [
|
||||
{
|
||||
"inputName": "input",
|
||||
"inputValue": "test"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.evaluation",
|
||||
"typeVersion": 4.7,
|
||||
"position": [864, 0],
|
||||
"id": "e027893e-4150-42dc-8b1e-8c24f5060cd1",
|
||||
"name": "set inptus"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "checkIfEvaluating"
|
||||
},
|
||||
"type": "n8n-nodes-base.evaluation",
|
||||
"typeVersion": 4.7,
|
||||
"position": [416, 0],
|
||||
"id": "7cc3b2fb-985b-437d-a0c7-0375f3717ee1",
|
||||
"name": "Evaluation"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When fetching a dataset row": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Wait",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set outputs": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "set inptus",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Wait": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Evaluation",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Evaluation": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set outputs",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "f0e9801eba0feea6a9ddf9beeabe34b0843eae42a1dbc62eaadd68e8f576be64"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user