mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
test: Migrate Langchain e2e tests to Playwright (#19161)
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "POST",
|
||||
"path": "/v1/chat/completions",
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"model": "gpt-4.1-mini",
|
||||
"stream": false,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello!"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rawBytes": "eyJtb2RlbCI6ImdwdC00LjEtbWluaSIsInN0cmVhbSI6ZmFsc2UsIm1lc3NhZ2VzIjpbeyJyb2xlIjoidXNlciIsImNvbnRlbnQiOiJIZWxsbyEifV19"
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-request-id": ["req_b8f45478a83a4dd2b6d3d6c0df25b482"],
|
||||
"x-ratelimit-reset-tokens": ["0s"],
|
||||
"x-ratelimit-reset-requests": ["2ms"],
|
||||
"x-ratelimit-remaining-tokens": ["149999997"]
|
||||
},
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"id": "chatcmpl-CDWB6cgun4QRcijeXQYPQWTaqtLmN",
|
||||
"object": "chat.completion",
|
||||
"created": 1757337992,
|
||||
"model": "gpt-4.1-mini-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 9,
|
||||
"completion_tokens": 9,
|
||||
"total_tokens": 18,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_6d7dcc9a98"
|
||||
},
|
||||
"rawBytes": "ewogICJpZCI6ICJjaGF0Y21wbC1DRFdCNmNndW40UVJjaWplWFFZUFFXVGFxdExtTiIsCiAgIm9iamVjdCI6ICJjaGF0LmNvbXBsZXRpb24iLAogICJjcmVhdGVkIjogMTc1NzMzNzk5MiwKICAibW9kZWwiOiAiZ3B0LTQuMS1taW5pLTIwMjUtMDQtMTQiLAogICJjaG9pY2VzIjogWwogICAgewogICAgICAiaW5kZXgiOiAwLAogICAgICAibWVzc2FnZSI6IHsKICAgICAgICAicm9sZSI6ICJhc3Npc3RhbnQiLAogICAgICAgICJjb250ZW50IjogIkhlbGxvISBIb3cgY2FuIEkgYXNzaXN0IHlvdSB0b2RheT8iLAogICAgICAgICJyZWZ1c2FsIjogbnVsbCwKICAgICAgICAiYW5ub3RhdGlvbnMiOiBbXQogICAgICB9LAogICAgICAibG9ncHJvYnMiOiBudWxsLAogICAgICAiZmluaXNoX3JlYXNvbiI6ICJzdG9wIgogICAgfQogIF0sCiAgInVzYWdlIjogewogICAgInByb21wdF90b2tlbnMiOiA5LAogICAgImNvbXBsZXRpb25fdG9rZW5zIjogOSwKICAgICJ0b3RhbF90b2tlbnMiOiAxOCwKICAgICJwcm9tcHRfdG9rZW5zX2RldGFpbHMiOiB7CiAgICAgICJjYWNoZWRfdG9rZW5zIjogMCwKICAgICAgImF1ZGlvX3Rva2VucyI6IDAKICAgIH0sCiAgICAiY29tcGxldGlvbl90b2tlbnNfZGV0YWlscyI6IHsKICAgICAgInJlYXNvbmluZ190b2tlbnMiOiAwLAogICAgICAiYXVkaW9fdG9rZW5zIjogMCwKICAgICAgImFjY2VwdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMCwKICAgICAgInJlamVjdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMAogICAgfQogIH0sCiAgInNlcnZpY2VfdGllciI6ICJkZWZhdWx0IiwKICAic3lzdGVtX2ZpbmdlcnByaW50IjogImZwXzZkN2RjYzlhOTgiCn0K"
|
||||
}
|
||||
},
|
||||
"id": "1757337994261-unknown-host-POST-_v1_chat_completions-1561df08.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "POST",
|
||||
"path": "/v1/chat/completions",
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"model": "gpt-4.1-mini",
|
||||
"stream": false,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello!"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rawBytes": "eyJtb2RlbCI6ImdwdC00LjEtbWluaSIsInN0cmVhbSI6ZmFsc2UsIm1lc3NhZ2VzIjpbeyJyb2xlIjoidXNlciIsImNvbnRlbnQiOiJIZWxsbyEifV19"
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-request-id": ["req_11f2fb25bd0b4a758224bba6f1e68e50"],
|
||||
"x-ratelimit-reset-tokens": ["0s"],
|
||||
"x-ratelimit-reset-requests": ["2ms"],
|
||||
"x-ratelimit-remaining-tokens": ["149999995"],
|
||||
"x-ratelimit-remaining-requests": ["29999"],
|
||||
"x-ratelimit-limit-tokens": ["150000000"]
|
||||
},
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"id": "chatcmpl-CDWB7f4flZjMHmcuet4JNe5pRTfVM",
|
||||
"object": "chat.completion",
|
||||
"created": 1757337993,
|
||||
"model": "gpt-4.1-mini-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 9,
|
||||
"completion_tokens": 9,
|
||||
"total_tokens": 18,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_4fce0778af"
|
||||
},
|
||||
"rawBytes": "ewogICJpZCI6ICJjaGF0Y21wbC1DRFdCN2Y0Zmxaak1IbWN1ZXQ0Sk5lNXBSVGZWTSIsCiAgIm9iamVjdCI6ICJjaGF0LmNvbXBsZXRpb24iLAogICJjcmVhdGVkIjogMTc1NzMzNzk5MywKICAibW9kZWwiOiAiZ3B0LTQuMS1taW5pLTIwMjUtMDQtMTQiLAogICJjaG9pY2VzIjogWwogICAgewogICAgICAiaW5kZXgiOiAwLAogICAgICAibWVzc2FnZSI6IHsKICAgICAgICAicm9sZSI6ICJhc3Npc3RhbnQiLAogICAgICAgICJjb250ZW50IjogIkhlbGxvISBIb3cgY2FuIEkgYXNzaXN0IHlvdSB0b2RheT8iLAogICAgICAgICJyZWZ1c2FsIjogbnVsbCwKICAgICAgICAiYW5ub3RhdGlvbnMiOiBbXQogICAgICB9LAogICAgICAibG9ncHJvYnMiOiBudWxsLAogICAgICAiZmluaXNoX3JlYXNvbiI6ICJzdG9wIgogICAgfQogIF0sCiAgInVzYWdlIjogewogICAgInByb21wdF90b2tlbnMiOiA5LAogICAgImNvbXBsZXRpb25fdG9rZW5zIjogOSwKICAgICJ0b3RhbF90b2tlbnMiOiAxOCwKICAgICJwcm9tcHRfdG9rZW5zX2RldGFpbHMiOiB7CiAgICAgICJjYWNoZWRfdG9rZW5zIjogMCwKICAgICAgImF1ZGlvX3Rva2VucyI6IDAKICAgIH0sCiAgICAiY29tcGxldGlvbl90b2tlbnNfZGV0YWlscyI6IHsKICAgICAgInJlYXNvbmluZ190b2tlbnMiOiAwLAogICAgICAiYXVkaW9fdG9rZW5zIjogMCwKICAgICAgImFjY2VwdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMCwKICAgICAgInJlamVjdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMAogICAgfQogIH0sCiAgInNlcnZpY2VfdGllciI6ICJkZWZhdWx0IiwKICAic3lzdGVtX2ZpbmdlcnByaW50IjogImZwXzRmY2UwNzc4YWYiCn0K"
|
||||
}
|
||||
},
|
||||
"id": "1757337994532-unknown-host-POST-_v1_chat_completions-1561df08.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "POST",
|
||||
"path": "/v1/chat/completions",
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"model": "gpt-4.1-mini",
|
||||
"stream": false,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello!"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rawBytes": "eyJtb2RlbCI6ImdwdC00LjEtbWluaSIsInN0cmVhbSI6ZmFsc2UsIm1lc3NhZ2VzIjpbeyJyb2xlIjoidXNlciIsImNvbnRlbnQiOiJIZWxsbyEifV19"
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-request-id": ["req_2476c446b9a74442be397d438277a7c1"],
|
||||
"x-ratelimit-reset-tokens": ["0s"],
|
||||
"x-ratelimit-reset-requests": ["2ms"],
|
||||
"x-ratelimit-remaining-tokens": ["149999995"],
|
||||
"x-ratelimit-remaining-requests": ["29999"],
|
||||
"x-ratelimit-limit-tokens": ["150000000"]
|
||||
},
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"id": "chatcmpl-CDWB6wRC1FBoWeZr36clt56dB1EzT",
|
||||
"object": "chat.completion",
|
||||
"created": 1757337992,
|
||||
"model": "gpt-4.1-mini-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 9,
|
||||
"completion_tokens": 9,
|
||||
"total_tokens": 18,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_4fce0778af"
|
||||
},
|
||||
"rawBytes": "ewogICJpZCI6ICJjaGF0Y21wbC1DRFdCNndSQzFGQm9XZVpyMzZjbHQ1NmRCMUV6VCIsCiAgIm9iamVjdCI6ICJjaGF0LmNvbXBsZXRpb24iLAogICJjcmVhdGVkIjogMTc1NzMzNzk5MiwKICAibW9kZWwiOiAiZ3B0LTQuMS1taW5pLTIwMjUtMDQtMTQiLAogICJjaG9pY2VzIjogWwogICAgewogICAgICAiaW5kZXgiOiAwLAogICAgICAibWVzc2FnZSI6IHsKICAgICAgICAicm9sZSI6ICJhc3Npc3RhbnQiLAogICAgICAgICJjb250ZW50IjogIkhlbGxvISBIb3cgY2FuIEkgYXNzaXN0IHlvdSB0b2RheT8iLAogICAgICAgICJyZWZ1c2FsIjogbnVsbCwKICAgICAgICAiYW5ub3RhdGlvbnMiOiBbXQogICAgICB9LAogICAgICAibG9ncHJvYnMiOiBudWxsLAogICAgICAiZmluaXNoX3JlYXNvbiI6ICJzdG9wIgogICAgfQogIF0sCiAgInVzYWdlIjogewogICAgInByb21wdF90b2tlbnMiOiA5LAogICAgImNvbXBsZXRpb25fdG9rZW5zIjogOSwKICAgICJ0b3RhbF90b2tlbnMiOiAxOCwKICAgICJwcm9tcHRfdG9rZW5zX2RldGFpbHMiOiB7CiAgICAgICJjYWNoZWRfdG9rZW5zIjogMCwKICAgICAgImF1ZGlvX3Rva2VucyI6IDAKICAgIH0sCiAgICAiY29tcGxldGlvbl90b2tlbnNfZGV0YWlscyI6IHsKICAgICAgInJlYXNvbmluZ190b2tlbnMiOiAwLAogICAgICAiYXVkaW9fdG9rZW5zIjogMCwKICAgICAgImFjY2VwdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMCwKICAgICAgInJlamVjdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMAogICAgfQogIH0sCiAgInNlcnZpY2VfdGllciI6ICJkZWZhdWx0IiwKICAic3lzdGVtX2ZpbmdlcnByaW50IjogImZwXzRmY2UwNzc4YWYiCn0K"
|
||||
}
|
||||
},
|
||||
"id": "1757337995023-unknown-host-POST-_v1_chat_completions-1561df08.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "POST",
|
||||
"path": "/v1/chat/completions",
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"model": "gpt-4.1-mini",
|
||||
"stream": false,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "calculator",
|
||||
"description": "Useful for getting the result of a math expression. The input to this tool should be a valid mathematical expression that could be executed by a simple calculator.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello!"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rawBytes": "eyJtb2RlbCI6ImdwdC00LjEtbWluaSIsInN0cmVhbSI6ZmFsc2UsInRvb2xzIjpbeyJ0eXBlIjoiZnVuY3Rpb24iLCJmdW5jdGlvbiI6eyJuYW1lIjoiY2FsY3VsYXRvciIsImRlc2NyaXB0aW9uIjoiVXNlZnVsIGZvciBnZXR0aW5nIHRoZSByZXN1bHQgb2YgYSBtYXRoIGV4cHJlc3Npb24uIFRoZSBpbnB1dCB0byB0aGlzIHRvb2wgc2hvdWxkIGJlIGEgdmFsaWQgbWF0aGVtYXRpY2FsIGV4cHJlc3Npb24gdGhhdCBjb3VsZCBiZSBleGVjdXRlZCBieSBhIHNpbXBsZSBjYWxjdWxhdG9yLiIsInBhcmFtZXRlcnMiOnsidHlwZSI6Im9iamVjdCIsInByb3BlcnRpZXMiOnsiaW5wdXQiOnsidHlwZSI6InN0cmluZyJ9fSwiYWRkaXRpb25hbFByb3BlcnRpZXMiOmZhbHNlLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIn19fV0sIm1lc3NhZ2VzIjpbeyJyb2xlIjoidXNlciIsImNvbnRlbnQiOiJIZWxsbyEifV19"
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-request-id": ["req_b9dd655cc0ac465980074d2301a4f058"],
|
||||
"x-ratelimit-reset-tokens": ["0s"],
|
||||
"x-ratelimit-reset-requests": ["2ms"],
|
||||
"x-ratelimit-remaining-tokens": ["149999995"],
|
||||
"x-ratelimit-remaining-requests": ["29999"],
|
||||
"x-ratelimit-limit-tokens": ["150000000"]
|
||||
},
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"id": "chatcmpl-CDWBAps4xOl2Ps9V9GQ7RwmRWYGnU",
|
||||
"object": "chat.completion",
|
||||
"created": 1757337996,
|
||||
"model": "gpt-4.1-mini-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 68,
|
||||
"completion_tokens": 10,
|
||||
"total_tokens": 78,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_4fce0778af"
|
||||
},
|
||||
"rawBytes": "ewogICJpZCI6ICJjaGF0Y21wbC1DRFdCQXBzNHhPbDJQczlWOUdRN1J3bVJXWUduVSIsCiAgIm9iamVjdCI6ICJjaGF0LmNvbXBsZXRpb24iLAogICJjcmVhdGVkIjogMTc1NzMzNzk5NiwKICAibW9kZWwiOiAiZ3B0LTQuMS1taW5pLTIwMjUtMDQtMTQiLAogICJjaG9pY2VzIjogWwogICAgewogICAgICAiaW5kZXgiOiAwLAogICAgICAibWVzc2FnZSI6IHsKICAgICAgICAicm9sZSI6ICJhc3Npc3RhbnQiLAogICAgICAgICJjb250ZW50IjogIkhlbGxvISBIb3cgY2FuIEkgYXNzaXN0IHlvdSB0b2RheT8iLAogICAgICAgICJyZWZ1c2FsIjogbnVsbCwKICAgICAgICAiYW5ub3RhdGlvbnMiOiBbXQogICAgICB9LAogICAgICAibG9ncHJvYnMiOiBudWxsLAogICAgICAiZmluaXNoX3JlYXNvbiI6ICJzdG9wIgogICAgfQogIF0sCiAgInVzYWdlIjogewogICAgInByb21wdF90b2tlbnMiOiA2OCwKICAgICJjb21wbGV0aW9uX3Rva2VucyI6IDEwLAogICAgInRvdGFsX3Rva2VucyI6IDc4LAogICAgInByb21wdF90b2tlbnNfZGV0YWlscyI6IHsKICAgICAgImNhY2hlZF90b2tlbnMiOiAwLAogICAgICAiYXVkaW9fdG9rZW5zIjogMAogICAgfSwKICAgICJjb21wbGV0aW9uX3Rva2Vuc19kZXRhaWxzIjogewogICAgICAicmVhc29uaW5nX3Rva2VucyI6IDAsCiAgICAgICJhdWRpb190b2tlbnMiOiAwLAogICAgICAiYWNjZXB0ZWRfcHJlZGljdGlvbl90b2tlbnMiOiAwLAogICAgICAicmVqZWN0ZWRfcHJlZGljdGlvbl90b2tlbnMiOiAwCiAgICB9CiAgfSwKICAic2VydmljZV90aWVyIjogImRlZmF1bHQiLAogICJzeXN0ZW1fZmluZ2VycHJpbnQiOiAiZnBfNGZjZTA3NzhhZiIKfQo="
|
||||
}
|
||||
},
|
||||
"id": "1757338000172-unknown-host-POST-_v1_chat_completions-fdac7829.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "POST",
|
||||
"path": "/v1/chat/completions",
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"model": "gpt-4.1-mini",
|
||||
"stream": false,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "calculator",
|
||||
"description": "Useful for getting the result of a math expression. The input to this tool should be a valid mathematical expression that could be executed by a simple calculator.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is 1000 * 10?"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rawBytes": "eyJtb2RlbCI6ImdwdC00LjEtbWluaSIsInN0cmVhbSI6ZmFsc2UsInRvb2xzIjpbeyJ0eXBlIjoiZnVuY3Rpb24iLCJmdW5jdGlvbiI6eyJuYW1lIjoiY2FsY3VsYXRvciIsImRlc2NyaXB0aW9uIjoiVXNlZnVsIGZvciBnZXR0aW5nIHRoZSByZXN1bHQgb2YgYSBtYXRoIGV4cHJlc3Npb24uIFRoZSBpbnB1dCB0byB0aGlzIHRvb2wgc2hvdWxkIGJlIGEgdmFsaWQgbWF0aGVtYXRpY2FsIGV4cHJlc3Npb24gdGhhdCBjb3VsZCBiZSBleGVjdXRlZCBieSBhIHNpbXBsZSBjYWxjdWxhdG9yLiIsInBhcmFtZXRlcnMiOnsidHlwZSI6Im9iamVjdCIsInByb3BlcnRpZXMiOnsiaW5wdXQiOnsidHlwZSI6InN0cmluZyJ9fSwiYWRkaXRpb25hbFByb3BlcnRpZXMiOmZhbHNlLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIn19fV0sIm1lc3NhZ2VzIjpbeyJyb2xlIjoidXNlciIsImNvbnRlbnQiOiJXaGF0IGlzIDEwMDAgKiAxMD8ifV19"
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-request-id": ["req_56023c245be3448c8373ff3aad21f7c4"],
|
||||
"x-ratelimit-reset-tokens": ["0s"],
|
||||
"x-ratelimit-reset-requests": ["2ms"],
|
||||
"x-ratelimit-remaining-tokens": ["149999992"],
|
||||
"x-ratelimit-remaining-requests": ["29999"],
|
||||
"x-ratelimit-limit-tokens": ["150000000"]
|
||||
},
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"id": "chatcmpl-CDWBDR6zMgzDe9t4hkJ0Xq3dxsK3y",
|
||||
"object": "chat.completion",
|
||||
"created": 1757337999,
|
||||
"model": "gpt-4.1-mini-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": null,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_88I326c3cCx7lOEXL3Wpp30c",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "calculator",
|
||||
"arguments": "{\"input\":\"1000 * 10\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "tool_calls"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 75,
|
||||
"completion_tokens": 18,
|
||||
"total_tokens": 93,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_4fce0778af"
|
||||
},
|
||||
"rawBytes": "ewogICJpZCI6ICJjaGF0Y21wbC1DRFdCRFI2ek1nekRlOXQ0aGtKMFhxM2R4c0szeSIsCiAgIm9iamVjdCI6ICJjaGF0LmNvbXBsZXRpb24iLAogICJjcmVhdGVkIjogMTc1NzMzNzk5OSwKICAibW9kZWwiOiAiZ3B0LTQuMS1taW5pLTIwMjUtMDQtMTQiLAogICJjaG9pY2VzIjogWwogICAgewogICAgICAiaW5kZXgiOiAwLAogICAgICAibWVzc2FnZSI6IHsKICAgICAgICAicm9sZSI6ICJhc3Npc3RhbnQiLAogICAgICAgICJjb250ZW50IjogbnVsbCwKICAgICAgICAidG9vbF9jYWxscyI6IFsKICAgICAgICAgIHsKICAgICAgICAgICAgImlkIjogImNhbGxfODhJMzI2YzNjQ3g3bE9FWEwzV3BwMzBjIiwKICAgICAgICAgICAgInR5cGUiOiAiZnVuY3Rpb24iLAogICAgICAgICAgICAiZnVuY3Rpb24iOiB7CiAgICAgICAgICAgICAgIm5hbWUiOiAiY2FsY3VsYXRvciIsCiAgICAgICAgICAgICAgImFyZ3VtZW50cyI6ICJ7XCJpbnB1dFwiOlwiMTAwMCAqIDEwXCJ9IgogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAicmVmdXNhbCI6IG51bGwsCiAgICAgICAgImFubm90YXRpb25zIjogW10KICAgICAgfSwKICAgICAgImxvZ3Byb2JzIjogbnVsbCwKICAgICAgImZpbmlzaF9yZWFzb24iOiAidG9vbF9jYWxscyIKICAgIH0KICBdLAogICJ1c2FnZSI6IHsKICAgICJwcm9tcHRfdG9rZW5zIjogNzUsCiAgICAiY29tcGxldGlvbl90b2tlbnMiOiAxOCwKICAgICJ0b3RhbF90b2tlbnMiOiA5MywKICAgICJwcm9tcHRfdG9rZW5zX2RldGFpbHMiOiB7CiAgICAgICJjYWNoZWRfdG9rZW5zIjogMCwKICAgICAgImF1ZGlvX3Rva2VucyI6IDAKICAgIH0sCiAgICAiY29tcGxldGlvbl90b2tlbnNfZGV0YWlscyI6IHsKICAgICAgInJlYXNvbmluZ190b2tlbnMiOiAwLAogICAgICAiYXVkaW9fdG9rZW5zIjogMCwKICAgICAgImFjY2VwdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMCwKICAgICAgInJlamVjdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMAogICAgfQogIH0sCiAgInNlcnZpY2VfdGllciI6ICJkZWZhdWx0IiwKICAic3lzdGVtX2ZpbmdlcnByaW50IjogImZwXzRmY2UwNzc4YWYiCn0K"
|
||||
}
|
||||
},
|
||||
"id": "1757338002750-unknown-host-POST-_v1_chat_completions-9d7fafef.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"httpRequest": {
|
||||
"method": "POST",
|
||||
"path": "/v1/chat/completions",
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"model": "gpt-4.1-mini",
|
||||
"stream": false,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "calculator",
|
||||
"description": "Useful for getting the result of a math expression. The input to this tool should be a valid mathematical expression that could be executed by a simple calculator.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is 1000 * 10?"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_88I326c3cCx7lOEXL3Wpp30c",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "calculator",
|
||||
"arguments": "{\"input\":\"1000 * 10\"}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"content": "10000",
|
||||
"tool_call_id": "call_88I326c3cCx7lOEXL3Wpp30c"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rawBytes": "eyJtb2RlbCI6ImdwdC00LjEtbWluaSIsInN0cmVhbSI6ZmFsc2UsInRvb2xzIjpbeyJ0eXBlIjoiZnVuY3Rpb24iLCJmdW5jdGlvbiI6eyJuYW1lIjoiY2FsY3VsYXRvciIsImRlc2NyaXB0aW9uIjoiVXNlZnVsIGZvciBnZXR0aW5nIHRoZSByZXN1bHQgb2YgYSBtYXRoIGV4cHJlc3Npb24uIFRoZSBpbnB1dCB0byB0aGlzIHRvb2wgc2hvdWxkIGJlIGEgdmFsaWQgbWF0aGVtYXRpY2FsIGV4cHJlc3Npb24gdGhhdCBjb3VsZCBiZSBleGVjdXRlZCBieSBhIHNpbXBsZSBjYWxjdWxhdG9yLiIsInBhcmFtZXRlcnMiOnsidHlwZSI6Im9iamVjdCIsInByb3BlcnRpZXMiOnsiaW5wdXQiOnsidHlwZSI6InN0cmluZyJ9fSwiYWRkaXRpb25hbFByb3BlcnRpZXMiOmZhbHNlLCIkc2NoZW1hIjoiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIn19fV0sIm1lc3NhZ2VzIjpbeyJyb2xlIjoidXNlciIsImNvbnRlbnQiOiJXaGF0IGlzIDEwMDAgKiAxMD8ifSx7InJvbGUiOiJhc3Npc3RhbnQiLCJjb250ZW50IjoiIiwidG9vbF9jYWxscyI6W3siaWQiOiJjYWxsXzg4STMyNmMzY0N4N2xPRVhMM1dwcDMwYyIsInR5cGUiOiJmdW5jdGlvbiIsImZ1bmN0aW9uIjp7Im5hbWUiOiJjYWxjdWxhdG9yIiwiYXJndW1lbnRzIjoie1wiaW5wdXRcIjpcIjEwMDAgKiAxMFwifSJ9fV19LHsicm9sZSI6InRvb2wiLCJjb250ZW50IjoiMTAwMDAiLCJ0b29sX2NhbGxfaWQiOiJjYWxsXzg4STMyNmMzY0N4N2xPRVhMM1dwcDMwYyJ9XX0="
|
||||
}
|
||||
},
|
||||
"httpResponse": {
|
||||
"statusCode": 200,
|
||||
"reasonPhrase": "OK",
|
||||
"headers": {
|
||||
"x-request-id": ["req_65b7f6b5aad740e0965555ae8df92d43"],
|
||||
"x-ratelimit-reset-tokens": ["0s"],
|
||||
"x-ratelimit-reset-requests": ["2ms"],
|
||||
"x-ratelimit-remaining-tokens": ["149999992"],
|
||||
"x-ratelimit-remaining-requests": ["29999"],
|
||||
"x-ratelimit-limit-tokens": ["150000000"]
|
||||
},
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"type": "JSON",
|
||||
"json": {
|
||||
"id": "chatcmpl-CDWBEPUWN3El2oq4aRaezkvUlRjv8",
|
||||
"object": "chat.completion",
|
||||
"created": 1757338000,
|
||||
"model": "gpt-4.1-mini-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "1000 multiplied by 10 equals 10,000.",
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 102,
|
||||
"completion_tokens": 13,
|
||||
"total_tokens": 115,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_4fce0778af"
|
||||
},
|
||||
"rawBytes": "ewogICJpZCI6ICJjaGF0Y21wbC1DRFdCRVBVV04zRWwyb3E0YVJhZXprdlVsUmp2OCIsCiAgIm9iamVjdCI6ICJjaGF0LmNvbXBsZXRpb24iLAogICJjcmVhdGVkIjogMTc1NzMzODAwMCwKICAibW9kZWwiOiAiZ3B0LTQuMS1taW5pLTIwMjUtMDQtMTQiLAogICJjaG9pY2VzIjogWwogICAgewogICAgICAiaW5kZXgiOiAwLAogICAgICAibWVzc2FnZSI6IHsKICAgICAgICAicm9sZSI6ICJhc3Npc3RhbnQiLAogICAgICAgICJjb250ZW50IjogIjEwMDAgbXVsdGlwbGllZCBieSAxMCBlcXVhbHMgMTAsMDAwLiIsCiAgICAgICAgInJlZnVzYWwiOiBudWxsLAogICAgICAgICJhbm5vdGF0aW9ucyI6IFtdCiAgICAgIH0sCiAgICAgICJsb2dwcm9icyI6IG51bGwsCiAgICAgICJmaW5pc2hfcmVhc29uIjogInN0b3AiCiAgICB9CiAgXSwKICAidXNhZ2UiOiB7CiAgICAicHJvbXB0X3Rva2VucyI6IDEwMiwKICAgICJjb21wbGV0aW9uX3Rva2VucyI6IDEzLAogICAgInRvdGFsX3Rva2VucyI6IDExNSwKICAgICJwcm9tcHRfdG9rZW5zX2RldGFpbHMiOiB7CiAgICAgICJjYWNoZWRfdG9rZW5zIjogMCwKICAgICAgImF1ZGlvX3Rva2VucyI6IDAKICAgIH0sCiAgICAiY29tcGxldGlvbl90b2tlbnNfZGV0YWlscyI6IHsKICAgICAgInJlYXNvbmluZ190b2tlbnMiOiAwLAogICAgICAiYXVkaW9fdG9rZW5zIjogMCwKICAgICAgImFjY2VwdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMCwKICAgICAgInJlamVjdGVkX3ByZWRpY3Rpb25fdG9rZW5zIjogMAogICAgfQogIH0sCiAgInNlcnZpY2VfdGllciI6ICJkZWZhdWx0IiwKICAic3lzdGVtX2ZpbmdlcnByaW50IjogImZwXzRmY2UwNzc4YWYiCn0K"
|
||||
}
|
||||
},
|
||||
"id": "1757338002751-unknown-host-POST-_v1_chat_completions-e8887e9d.json",
|
||||
"priority": 0,
|
||||
"timeToLive": {
|
||||
"unlimited": true
|
||||
},
|
||||
"times": {
|
||||
"unlimited": true
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@
|
||||
"n8n-containers": "workspace:*",
|
||||
"n8n-core": "workspace:*",
|
||||
"n8n-workflow": "workspace:*",
|
||||
"flatted": "catalog:",
|
||||
"nanoid": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"mockserver-client": "^5.15.0",
|
||||
|
||||
@@ -541,6 +541,28 @@ export class CanvasPage extends BasePage {
|
||||
await this.clickContextMenuAction('execute');
|
||||
}
|
||||
|
||||
async clearExecutionData(): Promise<void> {
|
||||
await this.page.getByTestId('clear-execution-data-button').click();
|
||||
}
|
||||
|
||||
getManualChatModal(): Locator {
|
||||
return this.page.getByTestId('canvas-chat');
|
||||
}
|
||||
|
||||
getManualChatInput(): Locator {
|
||||
return this.getManualChatModal().locator('.chat-inputs textarea');
|
||||
}
|
||||
|
||||
getManualChatMessages(): Locator {
|
||||
return this.getManualChatModal().locator('.chat-messages-list .chat-message');
|
||||
}
|
||||
|
||||
getManualChatLatestBotMessage(): Locator {
|
||||
return this.getManualChatModal()
|
||||
.locator('.chat-messages-list .chat-message.chat-message-from-bot')
|
||||
.last();
|
||||
}
|
||||
|
||||
getNodesWithSpinner(): Locator {
|
||||
return this.page.getByTestId('canvas-node').filter({
|
||||
has: this.page.locator('[data-icon=refresh-cw]'),
|
||||
@@ -560,6 +582,67 @@ export class CanvasPage extends BasePage {
|
||||
return this.page.locator('[data-test-id="canvas-node"].selected');
|
||||
}
|
||||
|
||||
// Disable node via context menu
|
||||
async disableNodeFromContextMenu(nodeName: string): Promise<void> {
|
||||
await this.rightClickNode(nodeName);
|
||||
await this.page
|
||||
.getByTestId('context-menu')
|
||||
.getByTestId('context-menu-item-toggle_activation')
|
||||
.click();
|
||||
}
|
||||
|
||||
// Chat open/close buttons (manual chat)
|
||||
async clickManualChatButton(): Promise<void> {
|
||||
await this.page.getByTestId('workflow-chat-button').click();
|
||||
await this.getManualChatModal().waitFor({ state: 'visible' });
|
||||
}
|
||||
|
||||
async closeManualChatModal(): Promise<void> {
|
||||
// Same toggle button closes the chat
|
||||
await this.page.getByTestId('workflow-chat-button').click();
|
||||
}
|
||||
|
||||
// Input plus endpoints (to add supplemental nodes to parent inputs)
|
||||
getInputPlusEndpointByType(nodeName: string, endpointType: string) {
|
||||
return this.page
|
||||
.locator(
|
||||
`[data-test-id="canvas-node-input-handle"][data-connection-type="${endpointType}"][data-node-name="${nodeName}"] [data-test-id="canvas-handle-plus"]`,
|
||||
)
|
||||
.first();
|
||||
}
|
||||
|
||||
// Generic supplemental node addition, then wrappers for specific types
|
||||
async addSupplementalNodeToParent(
|
||||
childNodeName: string,
|
||||
endpointType:
|
||||
| 'main'
|
||||
| 'ai_chain'
|
||||
| 'ai_document'
|
||||
| 'ai_embedding'
|
||||
| 'ai_languageModel'
|
||||
| 'ai_memory'
|
||||
| 'ai_outputParser'
|
||||
| 'ai_tool'
|
||||
| 'ai_retriever'
|
||||
| 'ai_textSplitter'
|
||||
| 'ai_vectorRetriever'
|
||||
| 'ai_vectorStore',
|
||||
parentNodeName: string,
|
||||
{ closeNDV = false, exactMatch = false }: { closeNDV?: boolean; exactMatch?: boolean } = {},
|
||||
): Promise<void> {
|
||||
await this.getInputPlusEndpointByType(parentNodeName, endpointType).click();
|
||||
|
||||
if (exactMatch) {
|
||||
await this.nodeCreatorNodeItems().getByText(childNodeName, { exact: true }).click();
|
||||
} else {
|
||||
await this.nodeCreatorNodeItems().filter({ hasText: childNodeName }).first().click();
|
||||
}
|
||||
|
||||
if (closeNDV) {
|
||||
await this.page.keyboard.press('Escape');
|
||||
}
|
||||
}
|
||||
|
||||
async openExecutions() {
|
||||
await this.page.getByTestId('radio-button-executions').click();
|
||||
}
|
||||
|
||||
67
packages/testing/playwright/pages/CredentialsEditModal.ts
Normal file
67
packages/testing/playwright/pages/CredentialsEditModal.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class CredentialsEditModal extends BasePage {
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
}
|
||||
|
||||
getModal(): Locator {
|
||||
return this.page.getByTestId('editCredential-modal');
|
||||
}
|
||||
|
||||
async waitForModal(): Promise<void> {
|
||||
await this.getModal().waitFor({ state: 'visible' });
|
||||
}
|
||||
|
||||
async fillField(key: string, value: string): Promise<void> {
|
||||
const input = this.page.getByTestId(`parameter-input-${key}`).locator('input, textarea');
|
||||
await input.fill(value);
|
||||
await expect(input).toHaveValue(value);
|
||||
}
|
||||
|
||||
async fillAllFields(values: Record<string, string>): Promise<void> {
|
||||
for (const [key, val] of Object.entries(values)) {
|
||||
await this.fillField(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
getSaveButton(): Locator {
|
||||
return this.page.getByTestId('credential-save-button');
|
||||
}
|
||||
|
||||
async save(): Promise<void> {
|
||||
const saveBtn = this.getSaveButton();
|
||||
await saveBtn.click();
|
||||
await saveBtn.waitFor({ state: 'visible' });
|
||||
|
||||
// Saved state changes the button text to "Saved"
|
||||
// Defensive wait for text when UI updates
|
||||
try {
|
||||
await saveBtn
|
||||
.getByText('Saved', { exact: true })
|
||||
.waitFor({ state: 'visible', timeout: 3000 });
|
||||
} catch {
|
||||
// ignore if text assertion is flaky; modal close below will still ensure flow continues
|
||||
}
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
const closeBtn = this.getModal().locator('.el-dialog__close').first();
|
||||
if (await closeBtn.isVisible()) {
|
||||
await closeBtn.click();
|
||||
}
|
||||
}
|
||||
|
||||
async setValues(values: Record<string, string>, save: boolean = true): Promise<void> {
|
||||
await this.waitForModal();
|
||||
await this.fillAllFields(values);
|
||||
|
||||
if (save) {
|
||||
await this.save();
|
||||
await this.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,10 +52,18 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
await this.clickByTestId('node-execute-button');
|
||||
}
|
||||
|
||||
getOutputPanel() {
|
||||
return this.page.getByTestId('output-panel');
|
||||
}
|
||||
|
||||
getContainer() {
|
||||
return this.page.getByTestId('ndv');
|
||||
}
|
||||
|
||||
getInputPanel() {
|
||||
return this.page.getByTestId('ndv-input-panel');
|
||||
}
|
||||
|
||||
getParameterExpressionPreviewValue() {
|
||||
return this.page.getByTestId('parameter-expression-preview-value');
|
||||
}
|
||||
@@ -81,6 +89,14 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
return this.page.getByTestId('run-data-pane-header');
|
||||
}
|
||||
|
||||
getOutputTable() {
|
||||
return this.getOutputPanel().getByTestId('ndv-data-container').locator('table');
|
||||
}
|
||||
|
||||
getOutputDataContainer() {
|
||||
return this.getOutputPanel().getByTestId('ndv-data-container');
|
||||
}
|
||||
|
||||
async setPinnedData(data: object | string) {
|
||||
const pinnedData = typeof data === 'string' ? data : JSON.stringify(data);
|
||||
await this.getEditPinnedDataButton().click();
|
||||
@@ -373,6 +389,14 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
await this.page.getByRole('option', { name: nodeName }).click();
|
||||
}
|
||||
|
||||
getInputTableHeader(index: number = 0) {
|
||||
return this.getInputPanel().locator('table th').nth(index);
|
||||
}
|
||||
|
||||
getInputTbodyCell(row: number, col: number) {
|
||||
return this.getInputPanel().locator('table tbody tr').nth(row).locator('td').nth(col);
|
||||
}
|
||||
|
||||
getAssignmentName(paramName: string, index = 0) {
|
||||
return this.getAssignmentCollectionContainer(paramName)
|
||||
.getByTestId('assignment')
|
||||
@@ -457,6 +481,14 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
await input.type(content);
|
||||
}
|
||||
|
||||
getInputTable() {
|
||||
return this.getInputPanel().locator('table');
|
||||
}
|
||||
|
||||
getInputTableCellSpan(row: number, col: number, dataName: string) {
|
||||
return this.getInputTbodyCell(row, col).locator(`span[data-name="${dataName}"]`).first();
|
||||
}
|
||||
|
||||
getAddFieldToSortByButton() {
|
||||
return this.getNodeParameters().getByText('Add Field To Sort By');
|
||||
}
|
||||
@@ -493,6 +525,46 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
await input.fill(value);
|
||||
}
|
||||
|
||||
async clickGetBackToCanvas(): Promise<void> {
|
||||
await this.clickBackToCanvasButton();
|
||||
}
|
||||
|
||||
getRunDataInfoCallout() {
|
||||
return this.page.getByTestId('run-data-callout');
|
||||
}
|
||||
|
||||
getOutputPanelTable() {
|
||||
return this.getOutputTable();
|
||||
}
|
||||
|
||||
async checkParameterCheckboxInputByName(name: string): Promise<void> {
|
||||
const checkbox = this.getParameterInput(name).locator('.el-switch.switch-input');
|
||||
await checkbox.click();
|
||||
}
|
||||
|
||||
// Credentials modal helpers
|
||||
async clickCreateNewCredential(eq: number = 0): Promise<void> {
|
||||
await this.page.getByTestId('node-credentials-select').nth(eq).click();
|
||||
await this.page.getByTestId('node-credentials-select-item-new').click();
|
||||
}
|
||||
|
||||
// Run selector and linking helpers
|
||||
getInputRunSelector() {
|
||||
return this.page.locator('[data-test-id="ndv-input-panel"] [data-test-id="run-selector"]');
|
||||
}
|
||||
|
||||
getOutputRunSelector() {
|
||||
return this.page.locator('[data-test-id="output-panel"] [data-test-id="run-selector"]');
|
||||
}
|
||||
|
||||
getInputRunSelectorInput() {
|
||||
return this.getInputRunSelector().locator('input');
|
||||
}
|
||||
|
||||
async toggleInputRunLinking(): Promise<void> {
|
||||
await this.getInputPanel().getByTestId('link-run').click();
|
||||
}
|
||||
|
||||
getNodeRunErrorMessage() {
|
||||
return this.page.getByTestId('node-error-message');
|
||||
}
|
||||
@@ -725,6 +797,19 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
getInputSelect() {
|
||||
return this.page.getByTestId('ndv-input-select').locator('input');
|
||||
}
|
||||
|
||||
getInputTableRows() {
|
||||
return this.getInputTable().locator('tr');
|
||||
}
|
||||
|
||||
getOutputRunSelectorInput() {
|
||||
return this.getOutputPanel().locator('[data-test-id="run-selector"] input');
|
||||
}
|
||||
|
||||
getAiOutputModeToggle() {
|
||||
return this.page.getByTestId('ai-output-mode-select');
|
||||
}
|
||||
|
||||
getCredentialLabel(credentialType: string) {
|
||||
return this.page.getByText(credentialType);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AIAssistantPage } from './AIAssistantPage';
|
||||
import { BecomeCreatorCTAPage } from './BecomeCreatorCTAPage';
|
||||
import { CanvasPage } from './CanvasPage';
|
||||
import { CommunityNodesPage } from './CommunityNodesPage';
|
||||
import { CredentialsEditModal } from './CredentialsEditModal';
|
||||
import { CredentialsPage } from './CredentialsPage';
|
||||
import { DemoPage } from './DemoPage';
|
||||
import { ExecutionsPage } from './ExecutionsPage';
|
||||
@@ -59,6 +60,7 @@ export class n8nPage {
|
||||
readonly workflowActivationModal: WorkflowActivationModal;
|
||||
readonly workflowSettingsModal: WorkflowSettingsModal;
|
||||
readonly workflowSharingModal: WorkflowSharingModal;
|
||||
readonly credentialsModal: CredentialsEditModal;
|
||||
|
||||
// Composables
|
||||
readonly workflowComposer: WorkflowComposer;
|
||||
@@ -98,6 +100,7 @@ export class n8nPage {
|
||||
// Modals
|
||||
this.workflowActivationModal = new WorkflowActivationModal(page);
|
||||
this.workflowSettingsModal = new WorkflowSettingsModal(page);
|
||||
this.credentialsModal = new CredentialsEditModal(page);
|
||||
|
||||
// Composables
|
||||
this.workflowComposer = new WorkflowComposer(this);
|
||||
|
||||
534
packages/testing/playwright/tests/ui/30-langchain.spec.ts
Normal file
534
packages/testing/playwright/tests/ui/30-langchain.spec.ts
Normal file
@@ -0,0 +1,534 @@
|
||||
import {
|
||||
AGENT_NODE_NAME,
|
||||
EDIT_FIELDS_SET_NODE_NAME,
|
||||
MANUAL_CHAT_TRIGGER_NODE_NAME,
|
||||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||
AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME,
|
||||
AI_MEMORY_POSTGRES_NODE_NAME,
|
||||
AI_TOOL_CALCULATOR_NODE_NAME,
|
||||
AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
|
||||
AI_TOOL_CODE_NODE_NAME,
|
||||
AI_TOOL_WIKIPEDIA_NODE_NAME,
|
||||
BASIC_LLM_CHAIN_NODE_NAME,
|
||||
CHAT_TRIGGER_NODE_DISPLAY_NAME,
|
||||
SCHEDULE_TRIGGER_NODE_NAME,
|
||||
} from '../../config/constants';
|
||||
import { test, expect } from '../../fixtures/base';
|
||||
import type { n8nPage } from '../../pages/n8nPage';
|
||||
|
||||
// Helper functions for common operations
|
||||
async function addOpenAILanguageModelWithCredentials(
|
||||
n8n: n8nPage,
|
||||
parentNode: string,
|
||||
options: { exactMatch?: boolean; closeNDV?: boolean } = { exactMatch: true, closeNDV: false },
|
||||
) {
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||
'ai_languageModel',
|
||||
parentNode,
|
||||
options,
|
||||
);
|
||||
|
||||
await n8n.ndv.clickCreateNewCredential();
|
||||
await n8n.credentialsModal.setValues({
|
||||
apiKey: 'abcd',
|
||||
});
|
||||
await n8n.ndv.clickBackToCanvasButton();
|
||||
}
|
||||
|
||||
async function waitForWorkflowSuccess(n8n: n8nPage, timeout = 3000) {
|
||||
await n8n.notifications.waitForNotificationAndClose('Workflow executed successfully', {
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
|
||||
async function executeChatAndWaitForResponse(n8n: n8nPage, message: string) {
|
||||
await n8n.canvas.logsPanel.sendManualChatMessage(message);
|
||||
await waitForWorkflowSuccess(n8n);
|
||||
}
|
||||
|
||||
async function verifyChatMessages(n8n: n8nPage, expectedCount: number, inputMessage?: string) {
|
||||
const messages = n8n.canvas.getManualChatMessages();
|
||||
await expect(messages).toHaveCount(expectedCount);
|
||||
if (inputMessage) {
|
||||
await expect(messages.first()).toContainText(inputMessage);
|
||||
}
|
||||
await expect(messages.last()).toBeVisible();
|
||||
return messages;
|
||||
}
|
||||
|
||||
async function verifyLogsPanelEntries(n8n: n8nPage, expectedEntries: string[]) {
|
||||
await expect(n8n.canvas.logsPanel.getLogEntries().first()).toBeVisible();
|
||||
await expect(n8n.canvas.logsPanel.getLogEntries()).toHaveCount(expectedEntries.length);
|
||||
for (let i = 0; i < expectedEntries.length; i++) {
|
||||
await expect(n8n.canvas.logsPanel.getLogEntries().nth(i)).toHaveText(expectedEntries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
async function setupBasicAgentWorkflow(n8n: n8nPage, additionalNodes: string[] = []) {
|
||||
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
|
||||
|
||||
// Add additional nodes if specified
|
||||
for (const nodeName of additionalNodes) {
|
||||
await n8n.canvas.addSupplementalNodeToParent(nodeName, 'ai_tool', AGENT_NODE_NAME, {
|
||||
closeNDV: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Always add OpenAI Language Model
|
||||
await addOpenAILanguageModelWithCredentials(n8n, AGENT_NODE_NAME);
|
||||
}
|
||||
|
||||
test.describe('Langchain Integration @capability:proxy', () => {
|
||||
test.beforeEach(async ({ n8n, proxyServer }) => {
|
||||
await proxyServer.clearAllExpectations();
|
||||
await proxyServer.loadExpectations('langchain');
|
||||
await n8n.canvas.openNewWorkflow();
|
||||
});
|
||||
|
||||
test.describe('Workflow Execution Behavior', () => {
|
||||
test('should not open chat modal', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||
'ai_languageModel',
|
||||
AGENT_NODE_NAME,
|
||||
{ exactMatch: true, closeNDV: true },
|
||||
);
|
||||
|
||||
await n8n.canvas.clickExecuteWorkflowButton();
|
||||
await expect(n8n.canvas.getManualChatModal()).toBeHidden();
|
||||
});
|
||||
|
||||
test('should remove test workflow button', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||
'ai_languageModel',
|
||||
AGENT_NODE_NAME,
|
||||
{ exactMatch: true, closeNDV: true },
|
||||
);
|
||||
|
||||
await n8n.canvas.disableNodeFromContextMenu(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
await expect(n8n.canvas.getExecuteWorkflowButton()).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Node Connection and Configuration', () => {
|
||||
test('should add nodes to all Agent node input types', async ({ n8n }) => {
|
||||
const agentSubNodes = [
|
||||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||
AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME,
|
||||
AI_TOOL_CALCULATOR_NODE_NAME,
|
||||
AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
|
||||
];
|
||||
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: false });
|
||||
|
||||
await n8n.ndv.checkParameterCheckboxInputByName('hasOutputParser');
|
||||
await n8n.ndv.clickBackToCanvasButton();
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||
'ai_languageModel',
|
||||
AGENT_NODE_NAME,
|
||||
{ exactMatch: true, closeNDV: true },
|
||||
);
|
||||
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME,
|
||||
'ai_memory',
|
||||
AGENT_NODE_NAME,
|
||||
{ closeNDV: true },
|
||||
);
|
||||
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_TOOL_CALCULATOR_NODE_NAME,
|
||||
'ai_tool',
|
||||
AGENT_NODE_NAME,
|
||||
{ closeNDV: true },
|
||||
);
|
||||
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
|
||||
'ai_outputParser',
|
||||
AGENT_NODE_NAME,
|
||||
{ closeNDV: true },
|
||||
);
|
||||
for (const nodeName of agentSubNodes) {
|
||||
await expect(n8n.canvas.connectionBetweenNodes(nodeName, AGENT_NODE_NAME)).toBeAttached();
|
||||
}
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2 + agentSubNodes.length); // Chat Trigger + Agent + 4 inputs
|
||||
});
|
||||
|
||||
test('should add multiple tool nodes to Agent node tool input type', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
|
||||
|
||||
const tools = [
|
||||
AI_TOOL_CALCULATOR_NODE_NAME,
|
||||
AI_TOOL_CODE_NODE_NAME,
|
||||
AI_TOOL_CODE_NODE_NAME,
|
||||
AI_TOOL_WIKIPEDIA_NODE_NAME,
|
||||
];
|
||||
|
||||
for (const tool of tools) {
|
||||
await n8n.canvas.addSupplementalNodeToParent(tool, 'ai_tool', AGENT_NODE_NAME, {
|
||||
closeNDV: true,
|
||||
});
|
||||
await expect(n8n.canvas.connectionBetweenNodes(tool, AGENT_NODE_NAME)).toBeAttached();
|
||||
}
|
||||
|
||||
// Chat Trigger + Agent + Tools
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2 + tools.length);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Auto-add Behavior', () => {
|
||||
test('should auto-add chat trigger and basic LLM chain when adding LLM node', async ({
|
||||
n8n,
|
||||
}) => {
|
||||
await n8n.canvas.addNode(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await expect(
|
||||
n8n.canvas.connectionBetweenNodes(
|
||||
CHAT_TRIGGER_NODE_DISPLAY_NAME,
|
||||
BASIC_LLM_CHAIN_NODE_NAME,
|
||||
),
|
||||
).toBeAttached();
|
||||
|
||||
await expect(
|
||||
n8n.canvas.connectionBetweenNodes(
|
||||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||
BASIC_LLM_CHAIN_NODE_NAME,
|
||||
),
|
||||
).toBeAttached();
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should not auto-add nodes if AI nodes are already present', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await n8n.canvas.addNode(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await expect(
|
||||
n8n.canvas.connectionBetweenNodes(CHAT_TRIGGER_NODE_DISPLAY_NAME, AGENT_NODE_NAME),
|
||||
).toBeAttached();
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should not auto-add nodes if ChatTrigger is already present', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(MANUAL_CHAT_TRIGGER_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await n8n.canvas.addNode(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await expect(
|
||||
n8n.canvas.connectionBetweenNodes(CHAT_TRIGGER_NODE_DISPLAY_NAME, AGENT_NODE_NAME),
|
||||
).toBeAttached();
|
||||
|
||||
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(3);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Chat Execution and Interaction', () => {
|
||||
test('should be able to open and execute Basic LLM Chain node', async ({ n8n }) => {
|
||||
await n8n.canvas.addNode(BASIC_LLM_CHAIN_NODE_NAME, { closeNDV: true });
|
||||
|
||||
await addOpenAILanguageModelWithCredentials(n8n, BASIC_LLM_CHAIN_NODE_NAME);
|
||||
|
||||
await n8n.canvas.openNode(BASIC_LLM_CHAIN_NODE_NAME);
|
||||
const inputMessage = 'Hello!';
|
||||
|
||||
await n8n.ndv.execute();
|
||||
await executeChatAndWaitForResponse(n8n, inputMessage);
|
||||
|
||||
// Verify chat message appears
|
||||
await expect(n8n.canvas.getManualChatLatestBotMessage()).toBeVisible();
|
||||
});
|
||||
test('should be able to open and execute Agent node', async ({ n8n }) => {
|
||||
await setupBasicAgentWorkflow(n8n);
|
||||
|
||||
const inputMessage = 'Hello!';
|
||||
await n8n.canvas.clickManualChatButton();
|
||||
await executeChatAndWaitForResponse(n8n, inputMessage);
|
||||
|
||||
// Verify chat message appears
|
||||
await expect(n8n.canvas.getManualChatLatestBotMessage()).toBeVisible();
|
||||
});
|
||||
test('should add and use Manual Chat Trigger node together with Agent node', async ({
|
||||
n8n,
|
||||
}) => {
|
||||
await setupBasicAgentWorkflow(n8n);
|
||||
|
||||
const inputMessage = 'Hello!';
|
||||
await n8n.canvas.clickManualChatButton();
|
||||
await executeChatAndWaitForResponse(n8n, inputMessage);
|
||||
|
||||
await verifyChatMessages(n8n, 2, inputMessage);
|
||||
await verifyLogsPanelEntries(n8n, [
|
||||
'When chat message received',
|
||||
'AI Agent',
|
||||
'OpenAI Chat Model',
|
||||
]);
|
||||
|
||||
await n8n.canvas.closeManualChatModal();
|
||||
await expect(n8n.canvas.logsPanel.getLogEntries()).toBeHidden();
|
||||
await expect(n8n.canvas.getManualChatInput()).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Tool Usage Notifications', () => {
|
||||
test('should show tool info notice if no existing tools were used during execution', async ({
|
||||
n8n,
|
||||
}) => {
|
||||
await setupBasicAgentWorkflow(n8n, [AI_TOOL_CALCULATOR_NODE_NAME]);
|
||||
await n8n.canvas.openNode(AGENT_NODE_NAME);
|
||||
|
||||
const inputMessage = 'Hello!';
|
||||
await n8n.ndv.execute();
|
||||
await executeChatAndWaitForResponse(n8n, inputMessage);
|
||||
|
||||
await n8n.canvas.closeManualChatModal();
|
||||
await n8n.canvas.openNode(AGENT_NODE_NAME);
|
||||
|
||||
await expect(n8n.ndv.getRunDataInfoCallout()).toBeVisible();
|
||||
});
|
||||
test('should not show tool info notice if tools were used during execution', async ({
|
||||
n8n,
|
||||
}) => {
|
||||
await n8n.canvas.addNode(MANUAL_CHAT_TRIGGER_NODE_NAME, { closeNDV: true });
|
||||
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: false });
|
||||
await expect(n8n.ndv.getRunDataInfoCallout()).toBeHidden();
|
||||
await n8n.ndv.clickBackToCanvasButton();
|
||||
|
||||
await addOpenAILanguageModelWithCredentials(n8n, AGENT_NODE_NAME);
|
||||
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_TOOL_CALCULATOR_NODE_NAME,
|
||||
'ai_tool',
|
||||
AGENT_NODE_NAME,
|
||||
{ closeNDV: true },
|
||||
);
|
||||
|
||||
const inputMessage = 'What is 1000 * 10?';
|
||||
await n8n.canvas.clickManualChatButton();
|
||||
await executeChatAndWaitForResponse(n8n, inputMessage);
|
||||
|
||||
await n8n.canvas.closeManualChatModal();
|
||||
await n8n.canvas.openNode(AGENT_NODE_NAME);
|
||||
|
||||
await expect(n8n.ndv.getRunDataInfoCallout()).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Error Handling and Logs Display', () => {
|
||||
// Helper function to set up the agent workflow with Postgres error configuration
|
||||
async function setupAgentWorkflowWithPostgresError(n8n: n8nPage) {
|
||||
await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });
|
||||
|
||||
// Add Calculator Tool (required for OpenAI model)
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_TOOL_CALCULATOR_NODE_NAME,
|
||||
'ai_tool',
|
||||
AGENT_NODE_NAME,
|
||||
{ closeNDV: true },
|
||||
);
|
||||
|
||||
// Add and configure Postgres Memory
|
||||
await n8n.canvas.addSupplementalNodeToParent(
|
||||
AI_MEMORY_POSTGRES_NODE_NAME,
|
||||
'ai_memory',
|
||||
AGENT_NODE_NAME,
|
||||
{ closeNDV: false },
|
||||
);
|
||||
|
||||
await n8n.ndv.clickCreateNewCredential();
|
||||
await n8n.credentialsModal.setValues({
|
||||
password: 'testtesttest',
|
||||
});
|
||||
|
||||
await n8n.ndv.getParameterInput('sessionIdType').click();
|
||||
await n8n.page.getByRole('option', { name: 'Define below' }).click();
|
||||
await n8n.ndv.getParameterInput('sessionKey').locator('input').fill('asdasd');
|
||||
await n8n.ndv.clickBackToCanvasButton();
|
||||
|
||||
// Add and configure OpenAI Language Model
|
||||
await addOpenAILanguageModelWithCredentials(n8n, AGENT_NODE_NAME);
|
||||
|
||||
await n8n.canvas.clickZoomToFitButton();
|
||||
}
|
||||
|
||||
// Helper function to assert logs tab is active
|
||||
async function assertLogsTabIsActive(n8n: n8nPage) {
|
||||
await expect(n8n.ndv.getOutputDataContainer()).toBeVisible();
|
||||
await expect(n8n.ndv.getAiOutputModeToggle()).toBeVisible();
|
||||
|
||||
const radioButtons = n8n.ndv.getAiOutputModeToggle().locator('[role="radio"]');
|
||||
await expect(radioButtons).toHaveCount(2);
|
||||
await expect(radioButtons.nth(1)).toHaveAttribute('aria-checked', 'true');
|
||||
}
|
||||
|
||||
// Helper function to assert error message is visible
|
||||
async function assertErrorMessageVisible(n8n: n8nPage) {
|
||||
await expect(
|
||||
n8n.ndv.getOutputPanel().getByTestId('node-error-message').first(),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
n8n.ndv.getOutputPanel().getByTestId('node-error-message').first(),
|
||||
).toContainText('Error in sub-node');
|
||||
}
|
||||
|
||||
test('should open logs tab by default when there was an error', async ({ n8n }) => {
|
||||
await setupAgentWorkflowWithPostgresError(n8n);
|
||||
|
||||
const inputMessage = 'Test the code tool';
|
||||
|
||||
// Execute workflow with chat trigger
|
||||
await n8n.canvas.clickManualChatButton();
|
||||
await executeChatAndWaitForResponse(n8n, inputMessage);
|
||||
|
||||
// Check that messages and logs are displayed
|
||||
const messages = await verifyChatMessages(n8n, 2, inputMessage);
|
||||
await expect(messages.last()).toContainText(
|
||||
'[ERROR: The service refused the connection - perhaps it is offline]',
|
||||
);
|
||||
|
||||
await expect(n8n.canvas.logsPanel.getLogEntries().first()).toBeVisible();
|
||||
await expect(n8n.canvas.logsPanel.getLogEntries()).toHaveCount(3);
|
||||
await expect(n8n.canvas.logsPanel.getSelectedLogEntry()).toHaveText('AI Agent');
|
||||
await expect(n8n.canvas.logsPanel.outputPanel.get()).toContainText(
|
||||
AI_MEMORY_POSTGRES_NODE_NAME,
|
||||
);
|
||||
|
||||
await n8n.canvas.closeManualChatModal();
|
||||
|
||||
// Open the AI Agent node to see the logs
|
||||
await n8n.canvas.openNode(AGENT_NODE_NAME);
|
||||
|
||||
// Assert that logs tab is active and error is displayed
|
||||
await assertLogsTabIsActive(n8n);
|
||||
await assertErrorMessageVisible(n8n);
|
||||
});
|
||||
|
||||
test('should switch to logs tab on error, when NDV is already opened', async ({ n8n }) => {
|
||||
// Remove the auto-added chat trigger
|
||||
await n8n.canvas.addNode(MANUAL_CHAT_TRIGGER_NODE_NAME, { closeNDV: false });
|
||||
|
||||
// Set manual trigger to output standard pinned data
|
||||
await n8n.ndv.getEditPinnedDataButton().click();
|
||||
await n8n.ndv.savePinnedData();
|
||||
await n8n.ndv.close();
|
||||
|
||||
// Set up the same workflow components but with manual trigger
|
||||
await setupAgentWorkflowWithPostgresError(n8n);
|
||||
|
||||
// Open the AI Agent node
|
||||
await n8n.canvas.openNode(AGENT_NODE_NAME);
|
||||
await n8n.ndv.getParameterInput('promptType').click();
|
||||
await n8n.page.getByRole('option', { name: 'Define below' }).click();
|
||||
await n8n.ndv.getParameterInput('text').locator('textarea').fill('Some text');
|
||||
await n8n.ndv.execute();
|
||||
await waitForWorkflowSuccess(n8n);
|
||||
|
||||
// Assert that logs tab is active and error is displayed
|
||||
await assertLogsTabIsActive(n8n);
|
||||
await assertErrorMessageVisible(n8n);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Advanced Workflow Features', () => {
|
||||
test('should render runItems for sub-nodes and allow switching between them', async ({
|
||||
n8n,
|
||||
}) => {
|
||||
await n8n.start.fromImportedWorkflow('In_memory_vector_store_fake_embeddings.json');
|
||||
await n8n.canvas.clickZoomToFitButton();
|
||||
await n8n.canvas.deselectAll();
|
||||
|
||||
await n8n.canvas.executeNode('Populate VS');
|
||||
await waitForWorkflowSuccess(n8n);
|
||||
|
||||
const assertInputOutputTextExists = async (text: string) => {
|
||||
await expect(n8n.ndv.getOutputPanel()).toContainText(text);
|
||||
await expect(n8n.ndv.getInputPanel()).toContainText(text);
|
||||
};
|
||||
|
||||
const assertInputOutputTextNotExists = async (text: string) => {
|
||||
await expect(n8n.ndv.getOutputPanel()).not.toContainText(text);
|
||||
await expect(n8n.ndv.getInputPanel()).not.toContainText(text);
|
||||
};
|
||||
|
||||
await n8n.canvas.openNode('Character Text Splitter');
|
||||
|
||||
await expect(n8n.ndv.getOutputRunSelector()).toBeVisible();
|
||||
await expect(n8n.ndv.getInputRunSelector()).toBeVisible();
|
||||
await expect(n8n.ndv.getInputRunSelectorInput()).toHaveValue('3 of 3');
|
||||
await expect(n8n.ndv.getOutputRunSelectorInput()).toHaveValue('3 of 3');
|
||||
await assertInputOutputTextExists('Kyiv');
|
||||
await assertInputOutputTextNotExists('Berlin');
|
||||
await assertInputOutputTextNotExists('Prague');
|
||||
|
||||
await n8n.ndv.changeOutputRunSelector('2 of 3');
|
||||
await assertInputOutputTextExists('Berlin');
|
||||
await assertInputOutputTextNotExists('Kyiv');
|
||||
await assertInputOutputTextNotExists('Prague');
|
||||
|
||||
await n8n.ndv.changeOutputRunSelector('1 of 3');
|
||||
await assertInputOutputTextExists('Prague');
|
||||
await assertInputOutputTextNotExists('Berlin');
|
||||
await assertInputOutputTextNotExists('Kyiv');
|
||||
|
||||
await n8n.ndv.toggleInputRunLinking();
|
||||
await n8n.ndv.changeOutputRunSelector('2 of 3');
|
||||
await expect(n8n.ndv.getInputRunSelectorInput()).toHaveValue('1 of 3');
|
||||
await expect(n8n.ndv.getOutputRunSelectorInput()).toHaveValue('2 of 3');
|
||||
await expect(n8n.ndv.getInputPanel()).toContainText('Prague');
|
||||
await expect(n8n.ndv.getInputPanel()).not.toContainText('Berlin');
|
||||
|
||||
await expect(n8n.ndv.getOutputPanel()).toContainText('Berlin');
|
||||
await expect(n8n.ndv.getOutputPanel()).not.toContainText('Prague');
|
||||
|
||||
await n8n.ndv.toggleInputRunLinking();
|
||||
await expect(n8n.ndv.getInputRunSelectorInput()).toHaveValue('1 of 3');
|
||||
await expect(n8n.ndv.getOutputRunSelectorInput()).toHaveValue('1 of 3');
|
||||
await assertInputOutputTextExists('Prague');
|
||||
await assertInputOutputTextNotExists('Berlin');
|
||||
await assertInputOutputTextNotExists('Kyiv');
|
||||
});
|
||||
|
||||
test('should execute up to Node 1 when using partial execution', async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_chat_partial_execution.json');
|
||||
await n8n.canvas.clickZoomToFitButton();
|
||||
|
||||
// Check that chat modal is not initially visible
|
||||
await expect(n8n.canvas.getManualChatModal().locator('main')).toBeHidden();
|
||||
|
||||
// Open Node 1 and execute it
|
||||
await n8n.canvas.openNode('Node 1');
|
||||
await n8n.ndv.execute();
|
||||
// Chat modal should now be visible
|
||||
await expect(n8n.canvas.getManualChatModal().locator('main')).toBeVisible();
|
||||
|
||||
// Send first message
|
||||
await n8n.canvas.logsPanel.sendManualChatMessage('Test');
|
||||
await expect(n8n.canvas.getManualChatLatestBotMessage()).toContainText('this_my_field_1');
|
||||
|
||||
// Refresh session
|
||||
await n8n.page.getByTestId('refresh-session-button').click();
|
||||
await expect(n8n.canvas.getManualChatMessages()).not.toBeAttached();
|
||||
|
||||
// Send another message
|
||||
await n8n.canvas.logsPanel.sendManualChatMessage('Another test');
|
||||
await expect(n8n.canvas.getManualChatLatestBotMessage()).toContainText('this_my_field_3');
|
||||
await expect(n8n.canvas.getManualChatLatestBotMessage()).toContainText('this_my_field_4');
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "535fd3dd-e78f-4ffa-a085-79723fc81b38",
|
||||
"name": "When chat message received",
|
||||
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [320, -380],
|
||||
"webhookId": "4fb58136-3481-494a-a30f-d9e064dac186"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "raw",
|
||||
"jsonOutput": "{\n \"this_my_field_1\": \"value\",\n \"this_my_field_2\": 1\n}\n",
|
||||
"options": {}
|
||||
},
|
||||
"id": "78201ec2-6def-40b7-85e5-97b580d7f642",
|
||||
"name": "Node 1",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [580, -380]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "raw",
|
||||
"jsonOutput": "{\n \"this_my_field_3\": \"value\",\n \"this_my_field_4\": 1\n}\n",
|
||||
"options": {}
|
||||
},
|
||||
"id": "1cfca06d-3ec3-427f-89f7-1ef321e025ff",
|
||||
"name": "Node 2",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [780, -380]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When chat message received": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Node 1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Node 1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Node 2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "178ef8a5109fc76c716d40bcadb720c455319f7b7a3fd5a39e4f336a091f524a"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user