[Recorder] soft-record (#7213)
This PR intends to add soft-record, a way to only record what hasn't changed. To be able to check whether the tests have changed, we check whether a MD5 hash of the test function's source code has changed from the previous time a recording stored this MD5 to the current test execution. Any new recording will store this MD5 into the recorded file. To only record the tests that have changed, the user must specify the environment variable TEST_MODE, with value "soft-record". Fixes #5156
This commit is contained in:
Родитель
f295a51ab6
Коммит
b67506f0a5
|
@ -134,7 +134,6 @@ dist
|
|||
dist-*
|
||||
test-dist
|
||||
test-dist*
|
||||
browser
|
||||
test-browser
|
||||
typings
|
||||
typedoc
|
||||
|
@ -150,3 +149,9 @@ swagger/*.json
|
|||
|
||||
# library-specific ignores
|
||||
sdk/keyvault/*/src/**/*.js
|
||||
|
||||
# skipping browser tests while also ignoring the browser folders where the browser bundles are
|
||||
/**/browser/*.js
|
||||
/**/browser/*.js.map
|
||||
/**/browser/*.html
|
||||
!/test/browser/*.spec.ts
|
|
@ -575,6 +575,12 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==
|
||||
/@types/md5/2.1.33:
|
||||
dependencies:
|
||||
'@types/node': 13.7.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-8+X960EtKLoSblhauxLKy3zzotagjoj3Jt1Tx9oaxUdZEPIBl+mkrUz6PNKpzJgkrKSN9YgkWTA29c0KnLshmA==
|
||||
/@types/mime-types/2.1.0:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -591,6 +597,18 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
|
||||
/@types/mock-fs/4.10.0:
|
||||
dependencies:
|
||||
'@types/node': 13.7.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-FQ5alSzmHMmliqcL36JqIA4Yyn9jyJKvRSGV3mvPh108VFatX7naJDzSG4fnFQNZFq9dIx0Dzoe6ddflMB2Xkg==
|
||||
/@types/mock-require/2.0.0:
|
||||
dependencies:
|
||||
'@types/node': 13.7.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-nOgjoE5bBiDeiA+z41i95makyHUSMWQMOPocP+J67Pqx/68HAXaeWN1NFtrAYYV6LrISIZZ8vKHm/a50k0f6Sg==
|
||||
/@types/nise/1.4.0:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -3309,6 +3327,10 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||
/get-caller-file/1.0.3:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
|
||||
/get-caller-file/2.0.5:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -5103,6 +5125,19 @@ packages:
|
|||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A==
|
||||
/mock-fs/4.10.4:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-gDfZDLaPIvtOusbusLinfx6YSe2YpQsDT8qdP41P47dQ/NQggtkHukz7hwqgt8QvMBmAv+Z6DGmXPyb5BWX2nQ==
|
||||
/mock-require/3.0.3:
|
||||
dependencies:
|
||||
get-caller-file: 1.0.3
|
||||
normalize-path: 2.1.1
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=4.3.0'
|
||||
resolution:
|
||||
integrity: sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==
|
||||
/moment/2.24.0:
|
||||
dev: false
|
||||
resolution:
|
||||
|
@ -5249,6 +5284,14 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
|
||||
/normalize-path/2.1.1:
|
||||
dependencies:
|
||||
remove-trailing-separator: 1.1.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
resolution:
|
||||
integrity: sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
|
||||
/normalize-path/3.0.0:
|
||||
dev: false
|
||||
engines:
|
||||
|
@ -6210,6 +6253,10 @@ packages:
|
|||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-MXW/jtHyl5F1PZI7NbpS8SOtympdLuF20aoWJT5lELR1p/HJDd5nqW8Eu9uLh/hCRY3FgvrIT5AwDCgBODklcA==
|
||||
/remove-trailing-separator/1.1.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
|
||||
/repeating/2.0.1:
|
||||
dependencies:
|
||||
is-finite: 1.0.2
|
||||
|
@ -8011,7 +8058,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/abort-controller'
|
||||
resolution:
|
||||
integrity: sha512-DO1D00rXpn1zUunQUSSF6VF1CU3nTbSMnoJo4y1y7/HoDjwgvq2Lifu64PscEv8Xs/EqejGGQUldCTlkwJ/wfw==
|
||||
integrity: sha512-Xla0VK+eEW8lnCzVyAL5++AoKQgNU1zHAUmoHSyMUCMpSG3KaUQiyOpfNyOG29yJ1r9I0z71m30NzRZtHFm39Q==
|
||||
tarball: 'file:projects/abort-controller.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/ai-text-analytics.tgz':
|
||||
|
@ -8071,7 +8118,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/ai-text-analytics'
|
||||
resolution:
|
||||
integrity: sha512-s4AvJH9H5CinaWgZKeL8P2UwpEL7sa5g4J51gAe/Y1AidxkZpqDLlD2m3aR6Ci/ioWwvrgzMCoz/vLvBEp0Frg==
|
||||
integrity: sha512-fqA10W69RAm4E1lXTZEX3wiCTIYcMG4BfLFL+VwKLdSIewSmK76a52JegKuKEMc6j05twqBDUyIiwbxGV9nG+g==
|
||||
tarball: 'file:projects/ai-text-analytics.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/app-configuration.tgz':
|
||||
|
@ -8113,7 +8160,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/app-configuration'
|
||||
resolution:
|
||||
integrity: sha512-zDI0h0tRBSFYpt6gYADkDF6OIIMgmxlFvtfQ3kg8/87lX14ci06bfDC7AFE7Sjx4qSgLmsWmJRjNSMJgdnMivg==
|
||||
integrity: sha512-oEvsSxFsumlUtpdWYLzhq20CKy9MmR34FYKbMQrhUBLHAloc+pznexh4u2/3OSdYcugDKgeicwkL35axbgGYKg==
|
||||
tarball: 'file:projects/app-configuration.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-amqp.tgz':
|
||||
|
@ -8181,7 +8228,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/core-amqp'
|
||||
resolution:
|
||||
integrity: sha512-9Z/u0i3Cu8SPqpRIWOFwraYSyrhspg/wiDBYgVcwSOHK0hA4oyUA+Y/CgqWtLm+luuTLNNJgRjFveAcxQ4OjTA==
|
||||
integrity: sha512-MhzxdzNAk3nDK+vSqVlrFWn9WzAlnYZKgixZopFUeLbNXKtTIuknEm4sbDQ8n/1svgzMDWN0PlsWnFYBW0WnBg==
|
||||
tarball: 'file:projects/core-amqp.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-arm.tgz':
|
||||
|
@ -8215,7 +8262,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/core-arm'
|
||||
resolution:
|
||||
integrity: sha512-0pn2mZyXmj//Y/nBrZacADv0y5cxhrtTmSDh7lJKBkO1mikLkWocQzfkXVQf2zDoZXRSqK5EDICnRx34Iij6og==
|
||||
integrity: sha512-gMo5M4yZopVded6wxND2JEGB6LyYP6FPbQYlPyhPFy36scETuxnOzg9d965CYEaLS/pa1Y8m73NvYPntfsFrAg==
|
||||
tarball: 'file:projects/core-arm.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-asynciterator-polyfill.tgz':
|
||||
|
@ -8233,7 +8280,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/core-asynciterator-polyfill'
|
||||
resolution:
|
||||
integrity: sha512-jDLggg/4sSiSb9xXQUsivEzh+w65cXOMPe/kPArXFs+LmAHaK1Et7soO3hxgmVW7AZ/5lVp8jcfW3hDXbKIFXQ==
|
||||
integrity: sha512-wcvU9xzocDcf6JqxBwlUIbDtU8XGBkq8n5TjQ4QYlRSFOUqjEX64a6XdMQr4dcga+OSoZw/QV/12HYPuUypK9w==
|
||||
tarball: 'file:projects/core-asynciterator-polyfill.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-auth.tgz':
|
||||
|
@ -8274,7 +8321,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/core-auth'
|
||||
resolution:
|
||||
integrity: sha512-s/939eY3zDHWsLuxyKMR6PfnRaA8fYeoDeAas83xmrRQwU5Nnj2YMfjWl8RwlUrLk4nJ2Hdi2nSZVp2UzCLyuw==
|
||||
integrity: sha512-SUk7DhjxjZhru6S3LTYJ7ZQjYaTKzrseBnzH632m9cRNiyYKN64ZQy7LlYBuvlFIFUL+6j3unoZlyEThr4SwfA==
|
||||
tarball: 'file:projects/core-auth.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-http.tgz':
|
||||
|
@ -8351,7 +8398,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/core-http'
|
||||
resolution:
|
||||
integrity: sha512-Upl8IMRO2gVy5hXFCz3YK0u90oSG2JVuf9fZ8ReWtRYvx9vcAFMvpFMEO0xrZnT7WKzdaT0sBcSs7pe8neHHAQ==
|
||||
integrity: sha512-Kkoja/RR+gTqQ3X7gjfiPpewtxKfj+tWnUlV95zcjlMQxqzbQ/j6lcH29NHhv52FznxnUhqzUvxYY05cs1cfXQ==
|
||||
tarball: 'file:projects/core-http.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-lro.tgz':
|
||||
|
@ -8407,7 +8454,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/core-lro'
|
||||
resolution:
|
||||
integrity: sha512-HzwPk4vzJPaKNDjWArURsZuV7oYXPjVd4ccxHSwTEJ2sSctuSUb3IMXinsNOKjJCHvTYJM6lNkESCuWIyDOTQQ==
|
||||
integrity: sha512-N+7SM1Yhnch618EhTBBoxQZRddrh+yqQhVuBJV0knDjtiC+nz+coerWGZ77T9tkTSgw4esZtZDn+MdTfQlKY0A==
|
||||
tarball: 'file:projects/core-lro.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-paging.tgz':
|
||||
|
@ -8425,7 +8472,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/core-paging'
|
||||
resolution:
|
||||
integrity: sha512-rRAaeqAsySfmVMMf46j6TljIPw2xVRZBVuyU9wrvQKJb1VzIEBZayG1jm9iTyyqUdFOEMn759//IfYP7KkhJNQ==
|
||||
integrity: sha512-HtQeUV6/jtHxcEJSGOLfZj58FrapAsPl4Lirod7Yib/yXMrDIBYkkkI3Wwx5mGap+6ChPvmsBeskFIii1ZKcJg==
|
||||
tarball: 'file:projects/core-paging.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-tracing.tgz':
|
||||
|
@ -8466,7 +8513,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/core-tracing'
|
||||
resolution:
|
||||
integrity: sha512-SJ+L9sNBKGjTh1Ye4OWZ1aqXJ534jIKQwi5eDRa6irbh1jSZsNUmSMnBxzkM3+0kfQZ8YP7jm6sjPykaOJ4hUQ==
|
||||
integrity: sha512-RWETP32JH6m1gxq4dQKAe572XiKX+bLAlU3/fUxPZPbscLNtphMy/E2DRzyvDCBEQfL+C49MxZIzEoy8ECdKgw==
|
||||
tarball: 'file:projects/core-tracing.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/cosmos.tgz':
|
||||
|
@ -8528,7 +8575,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/cosmos'
|
||||
resolution:
|
||||
integrity: sha512-GRoPrAKiHI521c+rxNT791bVFShNn6YefMT78uX5GqvTkxZa1jY3t5B2IdISXe0w1egUKwqImF2etEza6/pRZw==
|
||||
integrity: sha512-DKhpoSSi/f/Aq6de+2s/IpX1C5FQbVfBdK063cXTE1SCamCwzYdf6/SLLbWeEVadUgz4wFgBTIy9Ta3Zz79qUQ==
|
||||
tarball: 'file:projects/cosmos.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/event-hubs.tgz':
|
||||
|
@ -8608,7 +8655,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/event-hubs'
|
||||
resolution:
|
||||
integrity: sha512-7Hy1SDsby9EVOhWljTSZ6Z0BWd+YQqtrH5lXeIMxTRxVMP/0IxN2a69X/kSYm2Ey5nJ2Xf6I+lGhNRbJt2xTCw==
|
||||
integrity: sha512-ADxIBYXDACt52yTU4wn0dcR2pyKLNTlly7yIAD0e43929abQYDaznlMDTzGmjFtfPd3I3ndgc9LThNtse2Ks3A==
|
||||
tarball: 'file:projects/event-hubs.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/event-processor-host.tgz':
|
||||
|
@ -8666,7 +8713,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/event-processor-host'
|
||||
resolution:
|
||||
integrity: sha512-UKQZiZyDo3uX1TvimdPeny6Yc0s0t9foQLx1sap3CMzASBIocWrMXwYmxHWn3n8sRlOmMtqXNyJIy/1zru3+9A==
|
||||
integrity: sha512-SXJ4EJcxMdnl7GQuy7f7YXS4FLEcmojM48uJKlVQpbdFRSPZczCm8NAA8GGXJmJjVdq+nB388bYk2H724/ZTrA==
|
||||
tarball: 'file:projects/event-processor-host.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/eventhubs-checkpointstore-blob.tgz':
|
||||
|
@ -8731,7 +8778,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/eventhubs-checkpointstore-blob'
|
||||
resolution:
|
||||
integrity: sha512-lxxp1rmltAR87zw3K+swxDYnmXNHEOvxY7mW7cPSekQKfIIlzg/j83ZFluvO7481/HJR9JOabMcFS/VHxhPrgA==
|
||||
integrity: sha512-gjOffb+0MIjYerROkRE9jsYsEhRK8srYiK+JJbf8Qm4rcmz1cHOnbzO9CecOJ/p0abxOUhWDddaf8v2vPoeftg==
|
||||
tarball: 'file:projects/eventhubs-checkpointstore-blob.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/identity.tgz':
|
||||
|
@ -8787,7 +8834,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/identity'
|
||||
resolution:
|
||||
integrity: sha512-QOB2CcVr5P9knbZUzUVF3N1RpaJzjzwStfMspqmyDmpIdt4YOyXR5510/JuDBztGJE15Qk7m9dMUdnyVO7EoEg==
|
||||
integrity: sha512-mBOEy+pEAVgeJK+b9LM88IdJEnqqOdrEOIMVdprLpYzAILVVQ2dRLsO+bC5d3TfNK7ZttHz/jRmiGmRDanqobA==
|
||||
tarball: 'file:projects/identity.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/keyvault-certificates.tgz':
|
||||
|
@ -8853,7 +8900,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/keyvault-certificates'
|
||||
resolution:
|
||||
integrity: sha512-rev0ttXaoWIXgv7V0j0bWSlpW6mzJGO8s/FjprXf13cFaHrGL+mIilvY21TQ/PsYtX4H+B4MHzBBWv2rdCr4bg==
|
||||
integrity: sha512-HWTB3OFSaGPknGpAlUsMwPtznCjrGmQvyi5nwOPFLOGNklQTZIChUu3xlv7u/mm9woH7GDPIHJgFEf1N9kmTmg==
|
||||
tarball: 'file:projects/keyvault-certificates.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/keyvault-keys.tgz':
|
||||
|
@ -8919,7 +8966,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/keyvault-keys'
|
||||
resolution:
|
||||
integrity: sha512-4b5tCgtBSD3JTbMZaxyCAId+4EiRrtCApPIzg3EEZJgc1jXtJoycQbE12D9y9IizuHtHwxOk7fA93o6b61xpsg==
|
||||
integrity: sha512-hOvqlTGoEqwfvhUq7f/GzbLoWibGlncvQwNuVzkS1nm1/L4ECnJY+nePpj/uC4jZ5fAPiz4B6iFtiWN8RxBDRQ==
|
||||
tarball: 'file:projects/keyvault-keys.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/keyvault-secrets.tgz':
|
||||
|
@ -8985,7 +9032,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/keyvault-secrets'
|
||||
resolution:
|
||||
integrity: sha512-aJVju6YZCSpff4Pu6kT84sbKZirJbEblQNqUM0T3485sohXv7kLiiN2mms7mHr7NlYbXT/KUVmBj8ro5X7w0hw==
|
||||
integrity: sha512-76XNhCuQ+WVwljPMLrTJhBuu3mD5uBZ/IJB3WaUMpe+CP0t3puLXXoTQXy07+3mKmUcDqISasbVb1wATZfylyg==
|
||||
tarball: 'file:projects/keyvault-secrets.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/logger.tgz':
|
||||
|
@ -9039,7 +9086,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/logger'
|
||||
resolution:
|
||||
integrity: sha512-GCfPJg0YTmoF//OugUXBNudk+xG5zmVM0BDuBPU4aDRqIXGMuLJU6B4bHcSKar+Pp765IaboHZs4RzZuLOH02g==
|
||||
integrity: sha512-pEEw8s5yGhWD//xsHeJ68St44jvF7ByD6xgdI9MjUfmv86sdCdGb9Nx9SoPK9E3MS917ttPf36uqIfFwnfWs2Q==
|
||||
tarball: 'file:projects/logger.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/service-bus.tgz':
|
||||
|
@ -9120,7 +9167,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/service-bus'
|
||||
resolution:
|
||||
integrity: sha512-3PXValpAas0FSAxghJavB4SUvYBjh1BT+hzUYFfm1xYBaByzuluL0mETOpbH/OJgAEmwZBG/3IplddA1oxcaxQ==
|
||||
integrity: sha512-GoJsi+/mi4U/jHoL0CEadcHpttm7DzEmnJhZApIEYBA1UqIN8CMZchDwMFSPA0y4ma2oEfZJWCDBPwOU0PRz9w==
|
||||
tarball: 'file:projects/service-bus.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/storage-blob.tgz':
|
||||
|
@ -9181,7 +9228,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/storage-blob'
|
||||
resolution:
|
||||
integrity: sha512-lEP6M8gBMa0lo09vTP23CkzHXpID+MFTP9CABeoCVbl+GTUcagIfPpgwQnYyUkICzkXz7+nK9V8AWoLfaTn0TA==
|
||||
integrity: sha512-C4qCp2+U6+uWu3X5SKvXYGCYmX5UIuH/TjsQBqBwCr4bkNUxvs90E3QLzh8CS35JcTPt7kLtcg4cZml41IWg7g==
|
||||
tarball: 'file:projects/storage-blob.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/storage-file-datalake.tgz':
|
||||
|
@ -9250,7 +9297,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/storage-file-datalake'
|
||||
resolution:
|
||||
integrity: sha512-f/jaSqr2gkQ6Dlz//6fPvFbRxwRCbIkiq3pL1xkTvFF3c5y1pcF3lG1R48ZhnsSwpKuWDl0vfPEiM/4eWttbHQ==
|
||||
integrity: sha512-q7y9BkD1DaI1D+4ki8/8Qjsd3T3wikezl5jQnNb6yaBJPPgQqkPX+d9d7QzXfhEzcimJWZ1X4IplyZUu3MxJ9g==
|
||||
tarball: 'file:projects/storage-file-datalake.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/storage-file-share.tgz':
|
||||
|
@ -9311,7 +9358,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/storage-file-share'
|
||||
resolution:
|
||||
integrity: sha512-DJy4BcoAO/SElZYfGfL5Kdvt6A/Z0QkJ6pxZ7J0WyDFsjXEYroWnjLnNTU8T8akAMYWEVrm5j1b6eCueGUxing==
|
||||
integrity: sha512-x9NFeJIsOWCLb0xAEYcH0SykV8YgLEhEwObxq49k7iej28Q75OarDUfAhW2eIOx0vt1EfhOYj0PBTiOtO4z6OA==
|
||||
tarball: 'file:projects/storage-file-share.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/storage-queue.tgz':
|
||||
|
@ -9371,7 +9418,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/storage-queue'
|
||||
resolution:
|
||||
integrity: sha512-c2VKjOl+42FNBg0R1HVYtsfuiHWPANnRTzigDkBVYG9U6QIFAVE2/mWfQLdAQE/fEOjKL5swQSVeEwRLjXXHmQ==
|
||||
integrity: sha512-Y//E3m5VZRYZm6FlwOAcisq4hJi7z0MTqVh9ofhdg7Hc45qr31DBd8Ci8dn03poqaS3dSIGOTBObpYyfGB9Qgw==
|
||||
tarball: 'file:projects/storage-queue.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/template.tgz':
|
||||
|
@ -9422,7 +9469,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/template'
|
||||
resolution:
|
||||
integrity: sha512-eRzyhbFcnQZQgh8mnwyLFrZoP/wGTFoneLug+5s2AVEENNmr6+PQjEJVTg+dTlgfWG5gJP3meWTgHke3jcRt0A==
|
||||
integrity: sha512-Vw/0dXj2DcQ1NX0U+d/B2W7slh9HuoExmSU5EL0/BJClWsgIjJykyQ+wmwXafNF5lV/ltMPy7Rv+XbLft7K/tw==
|
||||
tarball: 'file:projects/template.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/test-utils-recorder.tgz':
|
||||
|
@ -9434,8 +9481,12 @@ packages:
|
|||
'@rollup/plugin-replace': 2.3.1_rollup@1.31.0
|
||||
'@types/chai': 4.2.8
|
||||
'@types/fs-extra': 8.0.1
|
||||
'@types/md5': 2.1.33
|
||||
'@types/mocha': 5.2.7
|
||||
'@types/mock-fs': 4.10.0
|
||||
'@types/mock-require': 2.0.0
|
||||
'@types/nise': 1.4.0
|
||||
'@types/node': 8.10.59
|
||||
chai: 4.2.0
|
||||
fs-extra: 8.1.0
|
||||
karma: 4.4.1
|
||||
|
@ -9451,9 +9502,12 @@ packages:
|
|||
karma-mocha: 1.3.0
|
||||
karma-mocha-reporter: 2.2.5_karma@4.4.1
|
||||
karma-remap-istanbul: 0.6.0_karma@4.4.1
|
||||
md5: 2.2.1
|
||||
mocha: 6.2.2
|
||||
mocha-junit-reporter: 1.23.3_mocha@6.2.2
|
||||
mocha-multi: 1.1.3_mocha@6.2.2
|
||||
mock-fs: 4.10.4
|
||||
mock-require: 3.0.3
|
||||
nise: 1.5.3
|
||||
nock: 11.7.2
|
||||
npm-run-all: 4.1.5
|
||||
|
@ -9470,7 +9524,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/test-utils-recorder'
|
||||
resolution:
|
||||
integrity: sha512-YXDtTZfZN4hHuqameLnHfJ9tjrnUAx8Ics3yDlDp+1XOGLZ83wTDEgNj5UL5ORDtegriqrELMT0f7n88QL2Gbw==
|
||||
integrity: sha512-BDMvFHxK4sXDE9cDKdYu+dNxmkpseGf/iGu+r6c4X0YBKYJ/eH9tKbOhXPi9sXjFgQeUkbPc0vRGsj+HS+/gsQ==
|
||||
tarball: 'file:projects/test-utils-recorder.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/testhub.tgz':
|
||||
|
@ -9491,7 +9545,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/testhub'
|
||||
resolution:
|
||||
integrity: sha512-guDU8PdEdKCVnGxNd1JEkmqukDoc1wodkEqQCWpY1+bX4ZT+ZY520gfVcMeMHYCEO8TAAhScGNke/y7p9qBArA==
|
||||
integrity: sha512-dW7m2LfMTGWZVxeZCvuHSzjBQBmz1868xK4zIx5AQifXDsBDrrQ3wdPHnarQV4yI0wcyzcI0QnVzRJ42++O4Hg==
|
||||
tarball: 'file:projects/testhub.tgz'
|
||||
version: 0.0.0
|
||||
registry: ''
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// https://github.com/karma-runner/karma-chrome-launcher
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
require("dotenv").config({ path: "../.env" });
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode } = require("@azure/test-utils-recorder");
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode, isSoftRecordMode, isRecordMode } = require("@azure/test-utils-recorder");
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
@ -28,7 +28,7 @@ module.exports = function(config) {
|
|||
// Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.keys
|
||||
"https://cdn.polyfill.io/v2/polyfill.js?features=Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.keys|always",
|
||||
"dist-test/index.browser.js"
|
||||
].concat(isPlaybackMode() ? ["recordings/browsers/**/*.json"] : []),
|
||||
].concat((isPlaybackMode() || isSoftRecordMode()) ? ["recordings/browsers/**/*.json"] : []),
|
||||
|
||||
exclude: [],
|
||||
|
||||
|
@ -99,7 +99,7 @@ module.exports = function(config) {
|
|||
browserDisconnectTolerance: 3,
|
||||
browserConsoleLogOptions: {
|
||||
// IMPORTANT: COMMENT the following line if you want to print debug logs in your browsers in record mode!!
|
||||
terminal: process.env.TEST_MODE !== "record"
|
||||
terminal: !isRecordMode()
|
||||
},
|
||||
|
||||
client: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// https://github.com/karma-runner/karma-chrome-launcher
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
require("dotenv").config({ path: "../.env" });
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode } = require("@azure/test-utils-recorder");
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode, isSoftRecordMode, isRecordMode } = require("@azure/test-utils-recorder");
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
@ -28,7 +28,7 @@ module.exports = function(config) {
|
|||
// Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.keys
|
||||
"https://cdn.polyfill.io/v2/polyfill.js?features=Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.keys|always",
|
||||
"dist-test/index.browser.js"
|
||||
].concat(isPlaybackMode() ? ["recordings/browsers/**/*.json"] : []),
|
||||
].concat((isPlaybackMode() || isSoftRecordMode()) ? ["recordings/browsers/**/*.json"] : []),
|
||||
|
||||
exclude: [],
|
||||
|
||||
|
@ -99,7 +99,7 @@ module.exports = function(config) {
|
|||
browserDisconnectTolerance: 3,
|
||||
browserConsoleLogOptions: {
|
||||
// IMPORTANT: COMMENT the following line if you want to print debug logs in your browsers in record mode!!
|
||||
terminal: process.env.TEST_MODE !== "record"
|
||||
terminal: !isRecordMode()
|
||||
},
|
||||
|
||||
client: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// https://github.com/karma-runner/karma-chrome-launcher
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
require("dotenv").config({ path: "../.env" });
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode } = require("@azure/test-utils-recorder");
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode, isSoftRecordMode, isRecordMode } = require("@azure/test-utils-recorder");
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
@ -28,7 +28,7 @@ module.exports = function(config) {
|
|||
// Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.keys
|
||||
"https://cdn.polyfill.io/v2/polyfill.js?features=Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.keys|always",
|
||||
"dist-test/index.browser.js"
|
||||
].concat(isPlaybackMode() ? ["recordings/browsers/**/*.json"] : []),
|
||||
].concat((isPlaybackMode() || isSoftRecordMode()) ? ["recordings/browsers/**/*.json"] : []),
|
||||
|
||||
exclude: [],
|
||||
|
||||
|
@ -99,7 +99,7 @@ module.exports = function(config) {
|
|||
browserDisconnectTolerance: 3,
|
||||
browserConsoleLogOptions: {
|
||||
// IMPORTANT: COMMENT the following line if you want to print debug logs in your browsers in record mode!!
|
||||
terminal: process.env.TEST_MODE !== "record"
|
||||
terminal: !isRecordMode()
|
||||
},
|
||||
|
||||
client: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// https://github.com/karma-runner/karma-chrome-launcher
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
require("dotenv").config({ path: "../.env" });
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode } = require("@azure/test-utils-recorder");
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode, isSoftRecordMode, isRecordMode } = require("@azure/test-utils-recorder");
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
@ -33,7 +33,7 @@ module.exports = function(config) {
|
|||
// Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.assign,Object.keys
|
||||
"https://cdn.polyfill.io/v2/polyfill.js?features=Symbol,Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.assign,Object.keys|always",
|
||||
"dist-test/index.browser.js"
|
||||
].concat(isPlaybackMode() ? ["recordings/browsers/**/*.json"] : []),
|
||||
].concat((isPlaybackMode() || isSoftRecordMode()) ? ["recordings/browsers/**/*.json"] : []),
|
||||
|
||||
// list of files / patterns to exclude
|
||||
exclude: [],
|
||||
|
@ -119,7 +119,7 @@ module.exports = function(config) {
|
|||
browserDisconnectTimeout: 10000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserConsoleLogOptions: {
|
||||
terminal: process.env.TEST_MODE !== "record"
|
||||
terminal: !isRecordMode()
|
||||
},
|
||||
|
||||
client: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// https://github.com/karma-runner/karma-chrome-launcher
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
require("dotenv").config({ path: "../.env" });
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode } = require("@azure/test-utils-recorder");
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode, isSoftRecordMode, isRecordMode } = require("@azure/test-utils-recorder");
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
@ -33,7 +33,7 @@ module.exports = function(config) {
|
|||
// Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.assign,Object.keys
|
||||
"https://cdn.polyfill.io/v2/polyfill.js?features=Symbol,Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.assign,Object.keys|always",
|
||||
"dist-test/index.browser.js"
|
||||
].concat(isPlaybackMode() ? ["recordings/browsers/**/*.json"] : []),
|
||||
].concat((isPlaybackMode() || isSoftRecordMode()) ? ["recordings/browsers/**/*.json"] : []),
|
||||
|
||||
// list of files / patterns to exclude
|
||||
exclude: [],
|
||||
|
@ -118,7 +118,7 @@ module.exports = function(config) {
|
|||
browserDisconnectTimeout: 10000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserConsoleLogOptions: {
|
||||
terminal: process.env.TEST_MODE !== "record"
|
||||
terminal: !isRecordMode()
|
||||
},
|
||||
|
||||
client: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// https://github.com/karma-runner/karma-chrome-launcher
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
require("dotenv").config({ path: "../.env" });
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode } = require("@azure/test-utils-recorder");
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode, isSoftRecordMode, isRecordMode } = require("@azure/test-utils-recorder");
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
@ -33,7 +33,7 @@ module.exports = function(config) {
|
|||
// Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.assign,Object.keys
|
||||
"https://cdn.polyfill.io/v2/polyfill.js?features=Symbol,Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.assign,Object.keys|always",
|
||||
"dist-test/index.browser.js"
|
||||
].concat(isPlaybackMode() ? ["recordings/browsers/**/*.json"] : []),
|
||||
].concat((isPlaybackMode() || isSoftRecordMode()) ? ["recordings/browsers/**/*.json"] : []),
|
||||
|
||||
// list of files / patterns to exclude
|
||||
exclude: [],
|
||||
|
@ -119,7 +119,7 @@ module.exports = function(config) {
|
|||
browserDisconnectTimeout: 10000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserConsoleLogOptions: {
|
||||
terminal: process.env.TEST_MODE !== "record"
|
||||
terminal: !isRecordMode()
|
||||
},
|
||||
|
||||
client: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// https://github.com/karma-runner/karma-chrome-launcher
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
require("dotenv").config({ path: "../.env" });
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode } = require("@azure/test-utils-recorder");
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode, isSoftRecordMode, isRecordMode } = require("@azure/test-utils-recorder");
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
@ -33,7 +33,7 @@ module.exports = function(config) {
|
|||
// Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.assign,Object.keys
|
||||
"https://cdn.polyfill.io/v2/polyfill.js?features=Symbol,Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.assign,Object.keys|always",
|
||||
"dist-test/index.browser.js"
|
||||
].concat(isPlaybackMode() ? ["recordings/browsers/**/*.json"] : []),
|
||||
].concat((isPlaybackMode() || isSoftRecordMode()) ? ["recordings/browsers/**/*.json"] : []),
|
||||
|
||||
// list of files / patterns to exclude
|
||||
exclude: [],
|
||||
|
@ -119,7 +119,7 @@ module.exports = function(config) {
|
|||
browserDisconnectTimeout: 10000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserConsoleLogOptions: {
|
||||
terminal: process.env.TEST_MODE !== "record"
|
||||
terminal: !isRecordMode()
|
||||
},
|
||||
|
||||
client: {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
## 2020-02-06
|
||||
|
||||
- If any URLs are meant to be replaced in the recordings with `replaceableVariables`, hostname from the URLs will be independently matched and replaced. [#7204](https://github.com/Azure/azure-sdk-for-js/issues/7204)
|
||||
|
||||
- Example - ENV_VAR `ACCOUNT_URL=https://azureaccount.com/` is supposed to be replaced with `https://endpoint/`, `azureaccount.com` will be independently matched and replaced with `endpoint` too.
|
||||
- Added the "soft-record" mode, which allows users to only record the tests that have changed. [#7213](https://github.com/Azure/azure-sdk-for-js/issues/7213)
|
||||
|
||||
|
||||
- [BUG FIX] Tests leveraging `coreHttp.requestOptions.timeout` with empty recordings(no nock scopes with request-responses defined in the recording) fail during the playback mode when executed along with other tests. Fixed the issue by resetting nock's global state. More info - [#7264](https://github.com/Azure/azure-sdk-for-js/issues/7264)
|
||||
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
- Tests hit the live-service
|
||||
- [Nock](https://www.npmjs.com/package/nock)/[Nise](https://www.npmjs.com/package/nise) are leveraged for recording the request-responses for future use
|
||||
- If recordings are already present, forces re-recording
|
||||
- The recorded files contain a hash of the test that executed that recording.
|
||||
- Some recorded files might not have the hash if they were generated by an older version of the recorder.
|
||||
- If TEST_MODE = "soft-record",
|
||||
- Tests will be skipped if they haven't changed. `soft-record` mode is isomorphous to `record` mode in all other aspects.
|
||||
- We determine whether a test has changed or not by doing a MD5 hash of the test's source code.
|
||||
- If the hash is not stored in the recordings, they will be re-recorded to include the hash.
|
||||
- Else If TEST_MODE = "live",
|
||||
- Tests hit the live-service, we don't record the requests/responses
|
||||
- Else If TEST_MODE = "playback" (or if the TEST_MODE is neither "record" nor "live"),
|
||||
|
|
|
@ -142,9 +142,9 @@ import { record, env, delay } from "@azure/test-utils-recorder";
|
|||
|
||||
The common recorder provides the following public methods and properties:
|
||||
|
||||
- `record`: Which deals with recording and playing back the network requests,
|
||||
- `record()`: Which deals with recording and playing back the network requests,
|
||||
depending on the value assigned to the `TEST_MODE` environment variable. If
|
||||
`TEST_MODE` equals to `record`, it will automatically store network requests
|
||||
`TEST_MODE` equals to `record` or `soft-record`, it will automatically store network requests
|
||||
in a plain text file in the folder `recordings` at the root of your
|
||||
repository (which for our example case is the root of the
|
||||
`@azure/keyvault/keyvault-keys` repository).
|
||||
|
@ -155,7 +155,7 @@ The common recorder provides the following public methods and properties:
|
|||
package in the repo. It also returns an object with a method `stop()`, which
|
||||
will allow you to control when you want the recorder to stop re-routing your
|
||||
http requests.
|
||||
- `Recorder`: The return of `record` is going to be an instance of the
|
||||
- `Recorder`: The return of the `record()` method is going to be an instance of the
|
||||
`Recorder`, which has some useful functions: `stop`, `skip`, `getUniqueName`,
|
||||
and `newDate`. `stop` will stop the recorder from storing a copy of the HTTP
|
||||
requests and responses. `skip` will pause the recorder only during the test
|
||||
|
@ -169,7 +169,9 @@ The common recorder provides the following public methods and properties:
|
|||
but only while the `TEST_MODE` is not `playback`, since you want to make sure you can run the playback tests
|
||||
as fast as possible.
|
||||
- `isRecordMode`, which is a shorthand for checking if the environment variable
|
||||
`TEST_MODE` is set to `record`.
|
||||
`TEST_MODE` is set to `record` or `soft-record`.
|
||||
- `isSoftRecordMode`, which is a shorthand for checking if the environment
|
||||
variable `TEST_MODE` is set to `soft-record` only.
|
||||
- `isPlaybackMode`, which is a shorthand for checking if the environment
|
||||
variable `TEST_MODE` is set to `playback`.
|
||||
- `setReplaceableVariables`, which will allow you to hide sensitive content
|
||||
|
@ -183,12 +185,12 @@ The common recorder provides the following public methods and properties:
|
|||
### Configuring your project
|
||||
|
||||
Having the common recorder as a devDependency means that you'll be able to start
|
||||
recording tests right away by using the exported method `record`. We'll get
|
||||
recording tests right away by using the exported method `record()`. We'll get
|
||||
into the details further down this document. This function will do recordings,
|
||||
or will play back previous recordings, depending on an environment variable:
|
||||
`TEST_MODE`. If the environment variable `TEST_MODE` is empty, `record` (and most
|
||||
`TEST_MODE`. If the environment variable `TEST_MODE` is empty, `record()` (and most
|
||||
of the functions provided by test-utils-recorder) won't be doing anything. You'll need
|
||||
to set this environment variable to `record` to start recording, and then to
|
||||
to set the `TEST_MODE` environment variable to `record` (or `soft-record`) to start recording, and then to
|
||||
`playback` to play the recordings back at your code.
|
||||
|
||||
#### package.json scripts
|
||||
|
@ -297,7 +299,7 @@ section of our guidelines:
|
|||
### How to record
|
||||
|
||||
To record your tests, make sure to set the environment variable `TEST_MODE` to
|
||||
`record`, then in your code, call to the `record` function exported from
|
||||
`record` or `soft-record`, then in your code, call to the `record()` function exported from
|
||||
`@azure/test-utils-recorder`, then call it before the http request you want to
|
||||
make. In the following example, we'll invoke the `record()` method before
|
||||
authenticating our KeyVault client:
|
||||
|
@ -336,6 +338,9 @@ After running this test with the `TEST_MODE` environment variable set to
|
|||
`recordings/node/my_test/recording_before_each_hook.js` with the contents of
|
||||
the HTTP request as well as the contents of the HTTP response.
|
||||
|
||||
If `TEST_MODE` is set to `soft-record` instead, the recorder will only create
|
||||
this recording file if the test has changed from a previous execution.
|
||||
|
||||
You'll see in the code above that we're invoking `recorder.stop`. This is so
|
||||
that, after each test, we can stop recording and the test file can be
|
||||
generated. We recommend creating new recorders on `beforeEach` and stopping the
|
||||
|
@ -391,6 +396,8 @@ request according to their matching copy stored in the recordings.
|
|||
|
||||
Once you have your recorded files, to update them after changing one of the tests, simply
|
||||
re-run the tests with `TEST_MODE` set to `record`. This will overwrite previously existing files.
|
||||
Or re run the tests with `TEST_MODE` set to `soft-record` to only overwrite the files related to
|
||||
tests that have changed.
|
||||
|
||||
> **Note:** If you rename the file of the test, or the name of the test, the
|
||||
> path of the recording will change. Make sure to delete the recordings
|
||||
|
@ -482,7 +489,7 @@ Some HTTP requests might have parameters with sensitive information. To get
|
|||
them out of your recordings, you can call to `skipQueryParams` with an array of strings
|
||||
where you specify the names of the query parameter you want to remove.
|
||||
|
||||
For example, give nthat we find this query parameters in our recordings:
|
||||
For example, given that we find this query parameters in our recordings:
|
||||
`?sv=2018-11-09&sr=c&sig=<sig>&sktid=<sktid>&skv=2018-11-09&se=2019-08-07T07%3A00%3A00Z&sp=rwdl`,
|
||||
if we don't want the parameters "sr", "sig" and "sp" to appear in these files, we can do the following:
|
||||
|
||||
|
|
|
@ -96,7 +96,15 @@ module.exports = function(config) {
|
|||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
// 'ChromeHeadless', 'Chrome', 'Firefox', 'Edge', 'IE'
|
||||
browsers: ["ChromeHeadless"],
|
||||
// --no-sandbox allows our tests to run in Linux without having to change the system.
|
||||
// --disable-web-security allows us to authenticate from the browser without having to write tests using interactive auth, which would be far more complex.
|
||||
browsers: ["ChromeHeadlessNoSandbox"],
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: "ChromeHeadless",
|
||||
flags: ["--no-sandbox", "--disable-web-security"]
|
||||
}
|
||||
},
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
|
@ -110,7 +118,8 @@ module.exports = function(config) {
|
|||
browserDisconnectTimeout: 10000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserConsoleLogOptions: {
|
||||
terminal: process.env.TEST_MODE !== "record"
|
||||
// We would usually hide the logs from the tests, but we don't need to do this inside of the recorder package because we are not recording the tests.
|
||||
// // terminal: process.env.TEST_MODE !== "record"
|
||||
},
|
||||
|
||||
client: {
|
||||
|
|
|
@ -71,9 +71,13 @@
|
|||
"@rollup/plugin-node-resolve": "^7.0.0",
|
||||
"@rollup/plugin-replace": "^2.2.0",
|
||||
"@types/fs-extra": "^8.0.0",
|
||||
"@types/chai": "^4.1.6",
|
||||
"@types/md5": "~2.1.33",
|
||||
"@types/mocha": "^5.2.5",
|
||||
"@types/nise": "^1.4.0",
|
||||
"@types/chai": "^4.1.6",
|
||||
"@types/node": "^8.0.0",
|
||||
"@types/mock-require": "~2.0.0",
|
||||
"@types/mock-fs": "~4.10.0",
|
||||
"chai": "^4.2.0",
|
||||
"karma": "^4.0.1",
|
||||
"karma-chrome-launcher": "^3.0.0",
|
||||
|
@ -88,9 +92,12 @@
|
|||
"karma-mocha": "^1.3.0",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-remap-istanbul": "^0.6.0",
|
||||
"md5": "^2.2.1",
|
||||
"mocha": "^6.2.2",
|
||||
"mocha-junit-reporter": "^1.18.0",
|
||||
"mocha-multi": "^1.1.3",
|
||||
"mock-fs": "^4.10.4",
|
||||
"mock-require": "^3.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nyc": "^14.0.0",
|
||||
"prettier": "^1.16.4",
|
||||
|
|
|
@ -10,10 +10,11 @@ import {
|
|||
parseUrl,
|
||||
isPlaybackMode,
|
||||
isRecordMode,
|
||||
findRecordingsFolderPath,
|
||||
RecorderEnvironmentSetup,
|
||||
filterSecretsFromStrings,
|
||||
filterSecretsRecursivelyFromJSON
|
||||
filterSecretsRecursivelyFromJSON,
|
||||
generateTestRecordingFilePath,
|
||||
nodeRequireRecordingIfExists
|
||||
} from "./utils";
|
||||
import { customConsoleLog } from "./customConsoleLog";
|
||||
|
||||
|
@ -60,33 +61,20 @@ export abstract class BaseRecorder {
|
|||
customizationsOnRecordings: [],
|
||||
queryParametersToSkip: []
|
||||
};
|
||||
protected hash: string;
|
||||
|
||||
constructor(platform: "node" | "browsers", testSuiteTitle: string, testTitle: string) {
|
||||
// File Extension
|
||||
// nock recordings for node tests - .js extension
|
||||
// recordings are saved in json format for browser tests - .json extension
|
||||
const ext = platform === "node" ? "js" : "json";
|
||||
// Filepath - `{node|browsers}/<describe-block-title>/recording_<test-title>.{js|json}`
|
||||
this.relativeTestRecordingFilePath =
|
||||
platform +
|
||||
"/" +
|
||||
this.formatPath(testSuiteTitle) +
|
||||
"/recording_" +
|
||||
this.formatPath(testTitle) +
|
||||
"." +
|
||||
ext;
|
||||
}
|
||||
|
||||
protected formatPath(path: string): string {
|
||||
return path
|
||||
.toLowerCase()
|
||||
.replace(/ /g, "_")
|
||||
.replace(/<=/g, "lte")
|
||||
.replace(/>=/g, "gte")
|
||||
.replace(/</g, "lt")
|
||||
.replace(/>/g, "gt")
|
||||
.replace(/=/g, "eq")
|
||||
.replace(/\W/g, "");
|
||||
constructor(
|
||||
platform: "node" | "browsers",
|
||||
hash: string,
|
||||
testSuiteTitle: string,
|
||||
testTitle: string
|
||||
) {
|
||||
this.hash = hash;
|
||||
this.relativeTestRecordingFilePath = generateTestRecordingFilePath(
|
||||
platform,
|
||||
testSuiteTitle,
|
||||
testTitle
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,8 +110,8 @@ export abstract class BaseRecorder {
|
|||
}
|
||||
|
||||
export class NockRecorder extends BaseRecorder {
|
||||
constructor(testSuiteTitle: string, testTitle: string) {
|
||||
super("node", testSuiteTitle, testTitle);
|
||||
constructor(hash: string, testSuiteTitle: string, testTitle: string) {
|
||||
super("node", hash, testSuiteTitle, testTitle);
|
||||
}
|
||||
|
||||
public record(recorderEnvironmentSetup: RecorderEnvironmentSetup): void {
|
||||
|
@ -133,7 +121,7 @@ export class NockRecorder extends BaseRecorder {
|
|||
});
|
||||
}
|
||||
|
||||
public playback(recorderEnvironmentSetup: RecorderEnvironmentSetup, filePath: string): void {
|
||||
public playback(recorderEnvironmentSetup: RecorderEnvironmentSetup, testFilePath: string): void {
|
||||
this.environmentSetup = recorderEnvironmentSetup;
|
||||
/**
|
||||
* `@azure/test-utils-recorder` package is used for both the browser and node tests
|
||||
|
@ -142,19 +130,12 @@ export class NockRecorder extends BaseRecorder {
|
|||
* `path` module is leveraged to import the node test recordings and `path` module can't be imported in the browser.
|
||||
* So, instead of `import`-ing the `path` library, `require` is being used and this code path is never executed in the browser.
|
||||
*
|
||||
* [A diiferent strategy is in place to import recordings for browser tests by leveraging `karma` plugins.]
|
||||
* [A different strategy is in place to import recordings for browser tests by leveraging `karma` plugins.]
|
||||
*/
|
||||
let path = require("path");
|
||||
// Get the full path of the `recordings` folder by navigating through the hierarchy of the test file path.
|
||||
const recordingsFolderPath = findRecordingsFolderPath(filePath);
|
||||
const recordingPath = path.resolve(recordingsFolderPath, this.relativeTestRecordingFilePath);
|
||||
if (fs.existsSync(recordingPath)) {
|
||||
this.uniqueTestInfo = require(recordingPath).testInfo;
|
||||
} else {
|
||||
throw new Error(
|
||||
`Recording (${this.relativeTestRecordingFilePath}) is not found at ${recordingsFolderPath}`
|
||||
);
|
||||
}
|
||||
this.uniqueTestInfo = nodeRequireRecordingIfExists(
|
||||
this.relativeTestRecordingFilePath,
|
||||
testFilePath
|
||||
).testInfo;
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
|
@ -163,6 +144,8 @@ export class NockRecorder extends BaseRecorder {
|
|||
const importNockStatement =
|
||||
"let nock = require('nock');\n" +
|
||||
"\n" +
|
||||
`module.exports.hash = "${this.hash}";\n` +
|
||||
"\n" +
|
||||
"module.exports.testInfo = " +
|
||||
JSON.stringify(this.uniqueTestInfo) +
|
||||
"\n";
|
||||
|
@ -232,8 +215,8 @@ export class NockRecorder extends BaseRecorder {
|
|||
export class NiseRecorder extends BaseRecorder {
|
||||
private recordings: any[] = [];
|
||||
|
||||
constructor(testSuiteTitle: string, testTitle: string) {
|
||||
super("browsers", testSuiteTitle, testTitle);
|
||||
constructor(hash: string, testSuiteTitle: string, testTitle: string) {
|
||||
super("browsers", hash, testSuiteTitle, testTitle);
|
||||
}
|
||||
|
||||
// Inserts a request/response pair into the recordings array
|
||||
|
@ -308,7 +291,7 @@ export class NiseRecorder extends BaseRecorder {
|
|||
|
||||
// 'onCreate' function is called when a new fake XMLHttpRequest object (req) is created
|
||||
// Our intent is to override the request's 'onreadystatechange' function so we can create a recording once the response is ready
|
||||
// We can only override 'onreadystatechange' AFTER the 'send' function is called because we need to make sure our implementation won't be overriden by the client
|
||||
// We can only override 'onreadystatechange' AFTER the 'send' function is called because we need to make sure our implementation won't be overridden by the client
|
||||
// But we can only override 'send' AFTER the 'open' function is called because the filter we set above makes Nise override it in 'open' body
|
||||
xhr.onCreate = function(req: any) {
|
||||
// We'll override the 'open' function, so we need to store a handle to its original implementation
|
||||
|
@ -339,7 +322,7 @@ export class NiseRecorder extends BaseRecorder {
|
|||
}
|
||||
};
|
||||
|
||||
// Now that we have overriden 'onreadystatechange', we can send the request to the server
|
||||
// Now that we have overridden 'onreadystatechange', we can send the request to the server
|
||||
reqSend.apply(req, arguments);
|
||||
};
|
||||
};
|
||||
|
@ -424,7 +407,8 @@ export class NiseRecorder extends BaseRecorder {
|
|||
path: "./recordings/" + this.relativeTestRecordingFilePath,
|
||||
content: {
|
||||
recordings: this.recordings,
|
||||
uniqueTestInfo: this.uniqueTestInfo
|
||||
uniqueTestInfo: this.uniqueTestInfo,
|
||||
hash: this.hash
|
||||
}
|
||||
})
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@ export {
|
|||
isPlaybackMode,
|
||||
isRecordMode,
|
||||
isLiveMode,
|
||||
isSoftRecordMode,
|
||||
RecorderEnvironmentSetup
|
||||
} from "./utils";
|
||||
export { jsonRecordingFilterFunction } from "./basekarma.conf";
|
||||
|
|
|
@ -7,7 +7,9 @@ import {
|
|||
isRecordMode,
|
||||
isPlaybackMode,
|
||||
RecorderEnvironmentSetup,
|
||||
env
|
||||
env,
|
||||
isSoftRecordMode,
|
||||
testHasChanged
|
||||
} from "./utils";
|
||||
import {
|
||||
NiseRecorder,
|
||||
|
@ -16,6 +18,7 @@ import {
|
|||
setEnvironmentOnLoad,
|
||||
setEnvironmentVariables
|
||||
} from "./baseRecorder";
|
||||
import MD5 from "md5";
|
||||
|
||||
/**
|
||||
* @export
|
||||
|
@ -28,7 +31,7 @@ export interface Recorder {
|
|||
*/
|
||||
stop(): void;
|
||||
/**
|
||||
* `{recorder.skip("node")}` and `{recorder.skip("browser")}` will skip the test in node.js and browser runtimes repectively.
|
||||
* `{recorder.skip("node")}` and `{recorder.skip("browser")}` will skip the test in node.js and browser runtimes respectively.
|
||||
* If the `{runtime}` is `{undefined}`, the test will be skipped in both the node and browser runtimes.
|
||||
* Has no effect in the live test mode.
|
||||
*/
|
||||
|
@ -88,12 +91,26 @@ export function record(
|
|||
testTitle = testContext.test!.title;
|
||||
}
|
||||
|
||||
const stringTest = testContext.currentTest!.fn!.toString();
|
||||
const currentHash = MD5(stringTest);
|
||||
const testAbsolutePath = testContext.currentTest!.file!;
|
||||
|
||||
if (
|
||||
isSoftRecordMode() &&
|
||||
!testHasChanged(testHierarchy, testTitle, testAbsolutePath, currentHash)
|
||||
) {
|
||||
testContext.test!.title = `${
|
||||
testContext.test!.title
|
||||
} (Test unchanged since last recording)`;
|
||||
testContext.skip();
|
||||
}
|
||||
|
||||
setEnvironmentOnLoad();
|
||||
|
||||
if (isBrowser()) {
|
||||
recorder = new NiseRecorder(testHierarchy, testTitle);
|
||||
recorder = new NiseRecorder(currentHash, testHierarchy, testTitle);
|
||||
} else {
|
||||
recorder = new NockRecorder(testHierarchy, testTitle);
|
||||
recorder = new NockRecorder(currentHash, testHierarchy, testTitle);
|
||||
}
|
||||
|
||||
if (isRecordMode()) {
|
||||
|
@ -103,18 +120,21 @@ export function record(
|
|||
} else if (isPlaybackMode()) {
|
||||
// If TEST_MODE=playback,
|
||||
// 1. sets up the ENV variables
|
||||
// 2. invokes the recorder, play the exisiting test recording.
|
||||
// 2. invokes the recorder, play the existing test recording.
|
||||
setEnvironmentVariables(env, recorderEnvironmentSetup.replaceableVariables);
|
||||
recorder.playback(recorderEnvironmentSetup, testContext.currentTest!.file!);
|
||||
recorder.playback(recorderEnvironmentSetup, testAbsolutePath);
|
||||
}
|
||||
// If TEST_MODE=live, hits the live-service and no recordings are generated.
|
||||
|
||||
return {
|
||||
stop: function() {
|
||||
recorder.stop();
|
||||
// We check wether we're on record or playback inside of the recorder's stop method.
|
||||
if (recorder) {
|
||||
recorder.stop();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* `{recorder.skip("node")}` and `{recorder.skip("browser")}` will skip the test in node.js and browser runtimes repectively.
|
||||
* `{recorder.skip("node")}` and `{recorder.skip("browser")}` will skip the test in node.js and browser runtimes respectively.
|
||||
* `{recorder.skip()}` If the `{runtime}` is undefined, the test will be skipped in both the node and browser runtimes.
|
||||
* @param runtime Can either be `"node"` or `"browser"` or `undefined`
|
||||
* @param reason Reason for skipping the test
|
||||
|
|
|
@ -54,7 +54,13 @@ export interface RecorderEnvironmentSetup {
|
|||
export const env = isBrowser() ? (window as any).__env__ : process.env;
|
||||
|
||||
export function isRecordMode() {
|
||||
return env.TEST_MODE === "record";
|
||||
// It should be safe to assume that these two can be considered being in record mode.
|
||||
// For more specific distinctions, one can use isSoftRecordMode.
|
||||
return env.TEST_MODE === "record" || isSoftRecordMode();
|
||||
}
|
||||
|
||||
export function isSoftRecordMode() {
|
||||
return env.TEST_MODE === "soft-record";
|
||||
}
|
||||
|
||||
export function isLiveMode() {
|
||||
|
@ -369,3 +375,85 @@ export function findRecordingsFolderPath(filePath: string): string {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function formatPath(path: string): string {
|
||||
return path
|
||||
.toLowerCase()
|
||||
.replace(/ /g, "_")
|
||||
.replace(/<=/g, "lte")
|
||||
.replace(/>=/g, "gte")
|
||||
.replace(/</g, "lt")
|
||||
.replace(/>/g, "gt")
|
||||
.replace(/=/g, "eq")
|
||||
.replace(/\W/g, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a file path with the following structure:
|
||||
*
|
||||
* `{node|browsers}/<describe-block-title>/recording_<test-title>.{js|json}`
|
||||
*
|
||||
* @param platform A string, either "node" or "browsers".
|
||||
* @param testSuiteTitle The title of the test suite.
|
||||
* @param testTitle The title of the specific test we're running.
|
||||
*/
|
||||
export function generateTestRecordingFilePath(
|
||||
platform: "node" | "browsers",
|
||||
testSuiteTitle: string,
|
||||
testTitle: string
|
||||
): string {
|
||||
// File Extension
|
||||
// nock recordings for node tests - .js extension
|
||||
// recordings are saved in json format for browser tests - .json extension
|
||||
const ext = platform === "node" ? "js" : "json";
|
||||
return `${platform}/${formatPath(testSuiteTitle)}/recording_${formatPath(testTitle)}.${ext}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires a file if it exists. Only works on NodeJS.
|
||||
*/
|
||||
export function nodeRequireRecordingIfExists(recordingPath: string, testAbsolutePath: string): any {
|
||||
if (isBrowser()) throw new Error("nodeRequireRecordingIfExists only works on NodeJS");
|
||||
const path = require("path");
|
||||
// Get the full path of the `recordings` folder by navigating through the hierarchy of the test file path.
|
||||
const recordingsFolderPath = findRecordingsFolderPath(testAbsolutePath);
|
||||
const absoluteRecordingPath = path.resolve(recordingsFolderPath, recordingPath);
|
||||
if (fs.existsSync(absoluteRecordingPath)) {
|
||||
return require(absoluteRecordingPath);
|
||||
} else {
|
||||
throw new Error(`The recording ${recordingPath} was not found in ${recordingsFolderPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a test hasn't changed from the last time it was recorded.
|
||||
* @param testContext
|
||||
* @param testSuiteTitle
|
||||
* @param testTitle
|
||||
* @param currentHash
|
||||
*/
|
||||
export function testHasChanged(
|
||||
testSuiteTitle: string,
|
||||
testTitle: string,
|
||||
testAbsolutePath: string,
|
||||
currentHash: string
|
||||
): boolean {
|
||||
const platform = isBrowser() ? "browsers" : "node";
|
||||
const recordingPath: string = generateTestRecordingFilePath(platform, testSuiteTitle, testTitle);
|
||||
|
||||
let previousHash: string = "";
|
||||
|
||||
if (platform === "node") {
|
||||
try {
|
||||
previousHash = nodeRequireRecordingIfExists(recordingPath, testAbsolutePath).hash;
|
||||
} catch (e) {}
|
||||
} else if ((window as any).__json__["recordings/" + recordingPath]) {
|
||||
previousHash = (window as any).__json__["recordings/" + recordingPath].hash;
|
||||
}
|
||||
|
||||
if (!previousHash) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return previousHash !== currentHash;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import { testHasChanged, generateTestRecordingFilePath } from "../../src/utils";
|
||||
import chai from "chai";
|
||||
const { expect } = chai;
|
||||
|
||||
describe("Browser utils", () => {
|
||||
describe("testHasChanged", () => {
|
||||
it("Should not crash if the recorded file doesn't exist", function() {
|
||||
const testSuiteTitle = this.test!.parent!.fullTitle();
|
||||
const testTitle = this.test!.title;
|
||||
|
||||
(window as any).__json__ = {};
|
||||
|
||||
// We won't be testing whether MD5 works or not.
|
||||
const newHash = "new hash";
|
||||
|
||||
expect(testHasChanged(testSuiteTitle, testTitle, "test/myTest.spec.ts", newHash)).to.equal(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("Should return true if the older hash doesn't exist", function() {
|
||||
const platform = "browsers";
|
||||
const testSuiteTitle = this.test!.parent!.fullTitle();
|
||||
const testTitle = this.test!.title;
|
||||
const filePath = generateTestRecordingFilePath(platform, testSuiteTitle, testTitle);
|
||||
|
||||
(window as any).__json__ = {
|
||||
["recordings/" + filePath]: {}
|
||||
};
|
||||
|
||||
// We won't be testing whether MD5 works or not.
|
||||
const newHash = "new hash";
|
||||
|
||||
expect(testHasChanged(testSuiteTitle, testTitle, "test/myTest.spec.ts", newHash)).to.equal(
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,204 @@
|
|||
import {
|
||||
generateTestRecordingFilePath,
|
||||
nodeRequireRecordingIfExists,
|
||||
isBrowser,
|
||||
findRecordingsFolderPath,
|
||||
testHasChanged
|
||||
} from "../../src/utils";
|
||||
import chai from "chai";
|
||||
const { expect } = chai;
|
||||
|
||||
describe("NodeJS utils", () => {
|
||||
describe("nodeRequireRecordingIfExists", () => {
|
||||
it("should be able to load the contents of a recording file if the file exists", function() {
|
||||
const mockFs = require("mock-fs");
|
||||
const mockRequire = require("mock-require");
|
||||
|
||||
// This needs to change if findRecordingsFolderPath changes.
|
||||
mockFs({
|
||||
// Our lazy require doesn't use the fs module internally.
|
||||
"recordings/recording.json": "",
|
||||
"test/myTest.spec.ts": "",
|
||||
"../../sdk/": {},
|
||||
"../../../rush.json": ""
|
||||
});
|
||||
|
||||
mockRequire("../recordings/recording.json", {
|
||||
property: "value"
|
||||
});
|
||||
|
||||
const path = require("path");
|
||||
const testAbsolutePath = path.resolve("test/myTest.spec.ts");
|
||||
|
||||
expect(nodeRequireRecordingIfExists("recording.json", testAbsolutePath).property).to.equal(
|
||||
"value"
|
||||
);
|
||||
|
||||
mockFs.restore();
|
||||
mockRequire.stopAll();
|
||||
});
|
||||
|
||||
it("should throw if the file at a given recording path doesn't exist", function() {
|
||||
if (isBrowser()) return this.skip();
|
||||
|
||||
// Require shouldn't be mocked in this test since we should be preventing require from being reached.
|
||||
|
||||
const mockFs = require("mock-fs");
|
||||
|
||||
// This needs to change if findRecordingsFolderPath changes.
|
||||
mockFs({
|
||||
recordings: {},
|
||||
"test/myTest.spec.ts": "",
|
||||
"../../sdk/": {},
|
||||
"../../../rush.json": ""
|
||||
});
|
||||
|
||||
const path = require("path");
|
||||
const testAbsolutePath = path.resolve("test/myTest.spec.ts");
|
||||
|
||||
let error: Error | undefined;
|
||||
|
||||
try {
|
||||
nodeRequireRecordingIfExists("recording.json", testAbsolutePath);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error!.message).to.equal(
|
||||
`The recording recording.json was not found in ${findRecordingsFolderPath(
|
||||
"recording.json"
|
||||
)}`
|
||||
);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("testHasChanged", () => {
|
||||
it("should not crash if the recorded file doesn't exist", function() {
|
||||
const mockFs = require("mock-fs");
|
||||
const mockRequire = require("mock-require");
|
||||
const testSuiteTitle = this.test!.parent!.fullTitle();
|
||||
const testTitle = this.test!.title;
|
||||
|
||||
// This needs to change if findRecordingsFolderPath changes.
|
||||
mockFs({
|
||||
// Our lazy require doesn't use the fs module internally.
|
||||
recordings: {},
|
||||
"test/myTest.spec.ts": "",
|
||||
"../../sdk/": {},
|
||||
"../../../rush.json": ""
|
||||
});
|
||||
|
||||
// We won't be testing whether MD5 works or not.
|
||||
const newHash = "new hash";
|
||||
|
||||
const path = require("path");
|
||||
const testAbsolutePath = path.resolve("test/myTest.spec.ts");
|
||||
|
||||
expect(testHasChanged(testSuiteTitle, testTitle, testAbsolutePath, newHash)).to.equal(true);
|
||||
|
||||
mockFs.restore();
|
||||
mockRequire.stopAll();
|
||||
});
|
||||
|
||||
it("should return true if the older hash doesn't exist", function() {
|
||||
const mockFs = require("mock-fs");
|
||||
const mockRequire = require("mock-require");
|
||||
const platform = "node";
|
||||
const testSuiteTitle = this.test!.parent!.fullTitle();
|
||||
const testTitle = this.test!.title;
|
||||
const filePath = generateTestRecordingFilePath(platform, testSuiteTitle, testTitle);
|
||||
|
||||
// This needs to change if findRecordingsFolderPath changes.
|
||||
mockFs({
|
||||
// Our lazy require doesn't use the fs module internally.
|
||||
[`recordings/${filePath}`]: "",
|
||||
"test/myTest.spec.ts": "",
|
||||
"../../sdk/": {},
|
||||
"../../../rush.json": ""
|
||||
});
|
||||
|
||||
mockRequire(`../recordings/${filePath}`, {});
|
||||
|
||||
// We won't be testing whether MD5 works or not.
|
||||
const newHash = "new hash";
|
||||
|
||||
const path = require("path");
|
||||
const testAbsolutePath = path.resolve("test/myTest.spec.ts");
|
||||
|
||||
expect(testHasChanged(testSuiteTitle, testTitle, testAbsolutePath, newHash)).to.equal(true);
|
||||
|
||||
mockFs.restore();
|
||||
mockRequire.stopAll();
|
||||
});
|
||||
|
||||
it("should return false if the older hash is the same as the new hash", function() {
|
||||
const mockFs = require("mock-fs");
|
||||
const mockRequire = require("mock-require");
|
||||
const platform = "node";
|
||||
const testSuiteTitle = this.test!.parent!.fullTitle();
|
||||
const testTitle = this.test!.title;
|
||||
const filePath = generateTestRecordingFilePath(platform, testSuiteTitle, testTitle);
|
||||
|
||||
// This needs to change if findRecordingsFolderPath changes.
|
||||
mockFs({
|
||||
// Our lazy require doesn't use the fs module internally.
|
||||
[`recordings/${filePath}`]: "",
|
||||
"test/myTest.spec.ts": "",
|
||||
"../../sdk/": {},
|
||||
"../../../rush.json": ""
|
||||
});
|
||||
|
||||
mockRequire(`../recordings/${filePath}`, {
|
||||
// We won't be testing whether MD5 works or not.
|
||||
hash: "same old hash"
|
||||
});
|
||||
|
||||
// We won't be testing whether MD5 works or not.
|
||||
const newHash = "same old hash";
|
||||
|
||||
const path = require("path");
|
||||
const testAbsolutePath = path.resolve("test/myTest.spec.ts");
|
||||
|
||||
expect(testHasChanged(testSuiteTitle, testTitle, testAbsolutePath, newHash)).to.equal(false);
|
||||
|
||||
mockFs.restore();
|
||||
mockRequire.stopAll();
|
||||
});
|
||||
|
||||
it("should return true if the older hash is different than the new hash", function() {
|
||||
const mockFs = require("mock-fs");
|
||||
const mockRequire = require("mock-require");
|
||||
const platform = "node";
|
||||
const testSuiteTitle = this.test!.parent!.fullTitle();
|
||||
const testTitle = this.test!.title;
|
||||
const filePath = generateTestRecordingFilePath(platform, testSuiteTitle, testTitle);
|
||||
|
||||
// This needs to change if findRecordingsFolderPath changes.
|
||||
mockFs({
|
||||
// Our lazy require doesn't use the fs module internally.
|
||||
[`recordings/${filePath}`]: "",
|
||||
"test/myTest.spec.ts": "",
|
||||
"../../sdk/": {},
|
||||
"../../../rush.json": ""
|
||||
});
|
||||
|
||||
mockRequire(`../recordings/${filePath}`, {
|
||||
// We won't be testing whether MD5 works or not.
|
||||
hash: "old hash"
|
||||
});
|
||||
|
||||
// We won't be testing whether MD5 works or not.
|
||||
const newHash = "new hash";
|
||||
|
||||
const path = require("path");
|
||||
const testAbsolutePath = path.resolve("test/myTest.spec.ts");
|
||||
|
||||
expect(testHasChanged(testSuiteTitle, testTitle, testAbsolutePath, newHash)).to.equal(true);
|
||||
|
||||
mockFs.restore();
|
||||
mockRequire.stopAll();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,7 +5,8 @@ import {
|
|||
encodeRFC3986,
|
||||
filterSecretsFromStrings,
|
||||
env,
|
||||
filterSecretsRecursivelyFromJSON
|
||||
filterSecretsRecursivelyFromJSON,
|
||||
generateTestRecordingFilePath
|
||||
} from "../src/utils";
|
||||
import chai from "chai";
|
||||
import { setEnvironmentVariables } from "../src/baseRecorder";
|
||||
|
@ -47,7 +48,7 @@ describe("utils", () => {
|
|||
});
|
||||
|
||||
describe("applyReplacementMap", () => {
|
||||
it("should filter URI encoded secrets", () => {
|
||||
it("Should filter URI encoded secrets", () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
SECRET: "(SECRET)"
|
||||
};
|
||||
|
@ -61,7 +62,7 @@ describe("utils", () => {
|
|||
expect(appliedMap).to.equal("azure.com/url/HIDDEN_SECRET");
|
||||
});
|
||||
|
||||
it("should filter hostname of the plain URI", () => {
|
||||
it("Should filter hostname of the plain URI", () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
ENDPOINT: "https://azureaccount.net/"
|
||||
};
|
||||
|
@ -75,7 +76,7 @@ describe("utils", () => {
|
|||
expect(appliedMap).to.equal("https://endpoint/");
|
||||
});
|
||||
|
||||
it("should filter hostname of the URI irrespective of `/` at the end", () => {
|
||||
it("Should filter hostname of the URI irrespective of `/` at the end", () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
ENDPOINT: "https://azureaccount.net/"
|
||||
};
|
||||
|
@ -89,7 +90,7 @@ describe("utils", () => {
|
|||
expect(appliedMap).to.equal("https://endpoint");
|
||||
});
|
||||
|
||||
it("should filter hostname of the URI irrespective of the content succeeding the hostname", () => {
|
||||
it("Should filter hostname of the URI irrespective of the content succeeding the hostname", () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
ENDPOINT: "https://azureaccount.net/queue/"
|
||||
};
|
||||
|
@ -103,7 +104,7 @@ describe("utils", () => {
|
|||
expect(appliedMap).to.equal("https://endpoint");
|
||||
});
|
||||
|
||||
it("should filter raw secrets", () => {
|
||||
it("Should filter raw secrets", () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
ENDPOINT: "azure.com/url/"
|
||||
};
|
||||
|
@ -117,7 +118,7 @@ describe("utils", () => {
|
|||
expect(appliedMap).to.equal("default.com/path/%28SECRET%29");
|
||||
});
|
||||
|
||||
it("should filter both, raw and URI encoded secrets", () => {
|
||||
it("Should filter both, raw and URI encoded secrets", () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
SECRET: "(SECRET)",
|
||||
ENDPOINT: "azure.com/url/"
|
||||
|
@ -133,7 +134,7 @@ describe("utils", () => {
|
|||
expect(appliedMap).to.equal("default.com/path/HIDDEN_SECRET");
|
||||
});
|
||||
|
||||
it("should work with recordings of several lines", () => {
|
||||
it("Should work with recordings of several lines", () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
SECRET: "(SECRET)",
|
||||
ENDPOINT: "azure.com/url/"
|
||||
|
@ -165,7 +166,7 @@ ultramarine.com/url/PUBLIC
|
|||
});
|
||||
|
||||
describe("applyReplacementFunctions", () => {
|
||||
it("should apply one replacement function", () => {
|
||||
it("Should apply one replacement function", () => {
|
||||
const replacements: Array<(content: string) => string> = [
|
||||
(source: string): string => {
|
||||
return source.replace(/banana/i, "Bonobo's");
|
||||
|
@ -176,7 +177,7 @@ ultramarine.com/url/PUBLIC
|
|||
expect(appliedFunctions).to.equal("Bonobo's Split");
|
||||
});
|
||||
|
||||
it("should apply several replacement functions", () => {
|
||||
it("Should apply several replacement functions", () => {
|
||||
const replacements: Array<(content: string) => string> = [
|
||||
(source: string): string => {
|
||||
return source.replace(/banana/i, "Bonobo's");
|
||||
|
@ -190,7 +191,7 @@ ultramarine.com/url/PUBLIC
|
|||
expect(appliedFunctions).to.equal("Bonobo's Flex");
|
||||
});
|
||||
|
||||
it("should work with recordings of several lines", () => {
|
||||
it("Should work with recordings of several lines", () => {
|
||||
const replacements = [
|
||||
(source: string): string => {
|
||||
return source.replace(/azure.com/g, "default.com");
|
||||
|
@ -234,7 +235,7 @@ ultramarine.com/url/PUBLIC
|
|||
expect(updatedRecording).to.deep.equal(expectedFilteredOutput);
|
||||
}
|
||||
|
||||
it("should work for strings", () => {
|
||||
it("Should work for strings", () => {
|
||||
env.SECRET = "SECRET";
|
||||
const replaceableVariables = { SECRET: "FAKE_IT" };
|
||||
|
||||
|
@ -243,7 +244,7 @@ ultramarine.com/url/PUBLIC
|
|||
expect(updatedRecording).to.equal("HERE_IS_THE_FLAG-FAKE_IT");
|
||||
});
|
||||
|
||||
it("should work for JSON content #1 - secret is present in the query attributes, part of the xml response string", () => {
|
||||
it("Should work for JSON content #1 - secret is present in the query attributes, part of the xml response string", () => {
|
||||
env.ACCOUNT_NAME = "azureaccount";
|
||||
const replaceableVariables = { ACCOUNT_NAME: "fakestorageaccount" };
|
||||
verifyFilterFunctionForJson(
|
||||
|
@ -286,7 +287,7 @@ ultramarine.com/url/PUBLIC
|
|||
);
|
||||
});
|
||||
|
||||
it("should work for JSON content #2 - secret is present as part of a JSON lookalike response string ", () => {
|
||||
it("Should work for JSON content #2 - secret is present as part of a JSON lookalike response string ", () => {
|
||||
verifyFilterFunctionForJson(
|
||||
{
|
||||
recording: [
|
||||
|
@ -312,7 +313,7 @@ ultramarine.com/url/PUBLIC
|
|||
);
|
||||
});
|
||||
|
||||
it("should work for JSON content #3 - array of JSON objects", () => {
|
||||
it("Should work for JSON content #3 - array of JSON objects", () => {
|
||||
env.ACCOUNT_NAME = "azureaccount";
|
||||
const replaceableVariables = { ACCOUNT_NAME: "fakestorageaccount" };
|
||||
verifyFilterFunctionForJson(
|
||||
|
@ -340,7 +341,7 @@ ultramarine.com/url/PUBLIC
|
|||
);
|
||||
});
|
||||
|
||||
it("should work for JSON content #4 - JSON content with key-value pair strings", () => {
|
||||
it("Should work for JSON content #4 - JSON content with key-value pair strings", () => {
|
||||
verifyFilterFunctionForJson(
|
||||
{
|
||||
response:
|
||||
|
@ -358,7 +359,7 @@ ultramarine.com/url/PUBLIC
|
|||
);
|
||||
});
|
||||
|
||||
it("should work for JSON content #5 - regex to be replaced is present as a key-value pair in the JSON content", () => {
|
||||
it("Should work for JSON content #5 - regex to be replaced is present as a key-value pair in the JSON content", () => {
|
||||
verifyFilterFunctionForJson(
|
||||
{ access_token: "eyJ0eXA75E_Q" },
|
||||
{},
|
||||
|
@ -370,7 +371,7 @@ ultramarine.com/url/PUBLIC
|
|||
);
|
||||
});
|
||||
|
||||
it("should work for JSON content #6 - JSON.stringify-ed content with regex to be replaced is present as a key-value pair at the top level in the JSON content", () => {
|
||||
it("Should work for JSON content #6 - JSON.stringify-ed content with regex to be replaced is present as a key-value pair at the top level in the JSON content", () => {
|
||||
verifyFilterFunctionForJson(
|
||||
JSON.stringify({ access_token: "eyJ0eXA75E_Q" }),
|
||||
{},
|
||||
|
@ -382,7 +383,7 @@ ultramarine.com/url/PUBLIC
|
|||
);
|
||||
});
|
||||
|
||||
it("should work for JSON content #7 - JSON.stringify-ed content - regex to be replaced is present as a key-value pair somewhere inside the tree in the JSON content", () => {
|
||||
it("Should work for JSON content #7 - JSON.stringify-ed content - regex to be replaced is present as a key-value pair somewhere inside the tree in the JSON content", () => {
|
||||
verifyFilterFunctionForJson(
|
||||
JSON.stringify({
|
||||
recording: [{ access_token: "eyJ0eXA75E_Q" }]
|
||||
|
@ -400,21 +401,21 @@ ultramarine.com/url/PUBLIC
|
|||
});
|
||||
|
||||
describe("set environment variables", () => {
|
||||
it("should not fail if the dictionary is empty", () => {
|
||||
it("Should not fail if the dictionary is empty", () => {
|
||||
env.SECRET = "SECRET";
|
||||
const replaceableVariables = {};
|
||||
|
||||
setEnvironmentVariables(env, replaceableVariables);
|
||||
});
|
||||
|
||||
it("should succeed if the dictionary has one key-value pair", () => {
|
||||
it("Should succeed if the dictionary has one key-value pair", () => {
|
||||
const replaceableVariables = { SECRET: "FAKE_IT" };
|
||||
|
||||
setEnvironmentVariables(env, replaceableVariables);
|
||||
expect(env.SECRET).to.equal("FAKE_IT");
|
||||
});
|
||||
|
||||
it("should succeed if the dictionary has multiple key-value pairs", () => {
|
||||
it("Should succeed if the dictionary has multiple key-value pairs", () => {
|
||||
const replaceableVariables = { ACCOUNT_NAME: "fake_account_name", SECRET: "FAKE IT" };
|
||||
|
||||
setEnvironmentVariables(env, replaceableVariables);
|
||||
|
@ -422,4 +423,26 @@ ultramarine.com/url/PUBLIC
|
|||
expect(env.ACCOUNT_NAME).to.equal("fake_account_name");
|
||||
});
|
||||
});
|
||||
|
||||
describe("generateTestRecordingFilePath", () => {
|
||||
it("Should generate a properly formatted path on platform: Node", function() {
|
||||
const platform = "node";
|
||||
const testSuiteTitle = this.test!.parent!.fullTitle();
|
||||
const testTitle = this.test!.title;
|
||||
const result = generateTestRecordingFilePath(platform, testSuiteTitle, testTitle);
|
||||
expect(result).to.equal(
|
||||
`${platform}/utils_generatetestrecordingfilepath/recording_should_generate_a_properly_formatted_path_on_platform_node.js`
|
||||
);
|
||||
});
|
||||
|
||||
it("Should generate a properly formatted path on platform: Browsers", function() {
|
||||
const platform = "browsers";
|
||||
const testSuiteTitle = this.test!.parent!.fullTitle();
|
||||
const testTitle = this.test!.title;
|
||||
const result = generateTestRecordingFilePath(platform, testSuiteTitle, testTitle);
|
||||
expect(result).to.equal(
|
||||
`${platform}/utils_generatetestrecordingfilepath/recording_should_generate_a_properly_formatted_path_on_platform_browsers.json`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// https://github.com/karma-runner/karma-chrome-launcher
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
require("dotenv").config();
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode } = require("@azure/test-utils-recorder");
|
||||
const { jsonRecordingFilterFunction, isPlaybackMode, isSoftRecordMode, isRecordMode } = require("@azure/test-utils-recorder");
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
@ -32,7 +32,7 @@ module.exports = function(config) {
|
|||
|
||||
// list of files / patterns to load in the browser
|
||||
files: ["dist-test/index.browser.js"].concat(
|
||||
isPlaybackMode() ? ["recordings/browsers/**/*.json"] : []
|
||||
(isPlaybackMode() || isSoftRecordMode()) ? ["recordings/browsers/**/*.json"] : []
|
||||
),
|
||||
|
||||
// list of files / patterns to exclude
|
||||
|
@ -127,7 +127,7 @@ module.exports = function(config) {
|
|||
browserDisconnectTimeout: 10000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserConsoleLogOptions: {
|
||||
terminal: process.env.TEST_MODE !== "record"
|
||||
terminal: !isRecordMode()
|
||||
},
|
||||
|
||||
client: {
|
||||
|
|
Загрузка…
Ссылка в новой задаче