extensions/snapshot/extension.js

  1. 'use strict'
  2. /**
  3. * @module extensions/snapshot/Snapshot
  4. */
  5. const prettyFormat = require('pretty-format')
  6. const snapshot = require('./snapshot')
  7. const clean = require('./clean')
  8. const statistics = require('./statistics')
  9. /**
  10. * Snapshot extension.
  11. *
  12. * @class
  13. */
  14. class Snapshot {
  15. /**
  16. * @param {Object} options - Options
  17. * @param {boolean} [options.updateSnapshots=false] - Should we update the snapshots
  18. * @param {boolean} [options.cleanSnapshots=false] - Should we clean the snapshot to remove unused snapshots
  19. */
  20. constructor(options) {
  21. this.options = options || {}
  22. this.shouldUpdate = this.options.updateSnapshots
  23. this.cleanSnapshots = this.options.cleanSnapshots
  24. this.featureFile = null
  25. this.scenarioLine = -1
  26. this._snapshotsCount = 0
  27. }
  28. /**
  29. * Compare a content to it's snapshot.
  30. * If no snapshot yet, it create it.
  31. *
  32. * It uses the context to name the snapshot: feature file, scenario name and nth snapshot of scenario
  33. * Snapshot name will be by default stored in FEATURE_FILE_FOLDER_PATH/__snapshots__/FEATURE_FILE_NAME.snap
  34. * And snapshot name will be "SCENARIO_NAME NUMBER_OF_TIME_SCNEARIO_NAME_APPEARD_IN_FEATURE.NUMBER_OF_TIME_WE_SNAPSHOTED_IN_CURRENT_SCENARIO"
  35. * For the first scenario of a scenario called "Scenario 1" that only appears once in feature file,
  36. * snapshot name will be "Scenario 1 1.1"
  37. *
  38. * If option "-u" or "--updateSnapshots" is used, all snapshots will be updated
  39. * If options "--cleanSnapshots" is used, unused stored snapshots will be removed.
  40. * @param {*} expectedContent - Content to compare to snapshot
  41. * @throws {string} If snapshot and expected content doesn't match, it throws diff between both
  42. */
  43. expectToMatch(expectedContent) {
  44. expectedContent = prettyFormat(expectedContent)
  45. let snapshotsFile = snapshot.snapshotsPath(this.featureFile, this.options)
  46. const scenarios = snapshot.extractScenarios(this.featureFile)
  47. const snapshotsPrefix = snapshot.prefixSnapshots(scenarios)[this.scenarioLine]
  48. if (!snapshotsPrefix)
  49. throw new Error(
  50. `Can not do a snapshot. Scenario not found in file ${this
  51. .featureFile} on line ${this.scenarioLine}`
  52. )
  53. this._snapshotsCount += 1
  54. const snapshotName = `${snapshotsPrefix.prefix}.${this._snapshotsCount}`
  55. if (this.cleanSnapshots) clean.referenceSnapshot(snapshotsFile, snapshotName) // To clean after all unreferenced snapshots
  56. const snapshotsContents = snapshot.readSnapshotFile(snapshotsFile)
  57. let snapshotContent = snapshotsContents[snapshotName]
  58. if (!snapshotContent) {
  59. statistics.created.push({ file: this.featureFile, name: snapshotName })
  60. } else if (this.shouldUpdate) {
  61. statistics.updated.push({ file: this.featureFile, name: snapshotName })
  62. }
  63. if (!snapshotContent || this.shouldUpdate) {
  64. snapshotsContents[snapshotName] = expectedContent
  65. snapshot.writeSnapshotFile(snapshotsFile, snapshotsContents)
  66. snapshotContent = expectedContent
  67. }
  68. const diff = snapshot.diff(snapshotContent, expectedContent)
  69. if (diff) throw new Error(diff)
  70. }
  71. }
  72. /**
  73. * Create a new isolated Snapshot module
  74. * @return {Snapshot}
  75. */
  76. module.exports = function(...args) {
  77. return new Snapshot(...args)
  78. }
  79. /**
  80. * Snapshot extension.
  81. * @type {Snapshot}
  82. */
  83. module.exports.Snapshot = Snapshot