Query Params JSON: JSON object of expected query-string parameters.
@@ -2039,7 +2039,7 @@
type="text"
placeholder="json"
spellcheck="false"
- @blur="persistFunctionMocks()"
+ @input="persistFunctionMocks({ silent: true })"
/>
Response Type: Optional response mode such as
@@ -2057,7 +2057,7 @@
class="native-textarea"
placeholder='{"field":"value"}'
spellcheck="false"
- @blur="persistFunctionMocks()"
+ @input="persistFunctionMocks({ silent: true })"
>
Response Body: JSON payload, data object, or exception body returned by the mock.
@@ -2198,6 +2198,7 @@
autocapitalize="off"
autocomplete="off"
autocorrect="off"
+ @input="syncEditorFromRawText(selectedDatasetKey, { silent: true })"
>
@@ -2231,6 +2232,7 @@
testFile: 'tests/wallets/test_rpc_wallets.py'
}
]
+ const STORAGE_PREFIX = 'lnbits-wallet-fixture-studio'
const JsonPanel = {
name: 'JsonPanel',
@@ -3064,19 +3066,39 @@
this.ensureSelections(this.selectedDatasetKey)
this.ensureSettingsDraftForCurrent()
this.ensureFunctionMockDraftForCurrent()
+ this.persistUiState()
},
currentFundingSourceName() {
this.ensureSettingsDraftForCurrent()
+ this.persistUiState()
},
currentTestFundingSourceName() {
this.ensureFunctionMockDraftForCurrent()
+ this.persistUiState()
},
currentFunctionName() {
this.ensureFunctionMockDraftForCurrent()
+ this.persistUiState()
+ },
+ currentSelectedTestTypeKey() {
+ this.persistUiState()
}
},
mounted() {
- this.loadRelative(this.selectedDatasetKey)
+ this.restoreUiState()
+
+ let restoredAny = this.restoreActiveDatasetFromStorage()
+ if (!restoredAny) {
+ for (const def of DATASET_DEFS) {
+ restoredAny = this.restoreDatasetFromStorage(def.key) || restoredAny
+ }
+ }
+
+ if (!this.datasets[this.selectedDatasetKey].analysis) {
+ this.loadRelative(this.selectedDatasetKey)
+ } else if (!restoredAny) {
+ this.loadRelative(this.selectedDatasetKey)
+ }
window.addEventListener('keydown', this.handleGlobalKeydown)
},
@@ -3084,6 +3106,224 @@
window.removeEventListener('keydown', this.handleGlobalKeydown)
},
methods: {
+ storageKey(datasetKey) {
+ return `${STORAGE_PREFIX}:dataset:${datasetKey}`
+ },
+ activeDatasetStorageKey() {
+ return `${STORAGE_PREFIX}:active-dataset`
+ },
+ uiStateStorageKey() {
+ return `${STORAGE_PREFIX}:ui-state`
+ },
+ canUseLocalStorage() {
+ try {
+ return typeof window !== 'undefined' && Boolean(window.localStorage)
+ } catch (error) {
+ return false
+ }
+ },
+ persistDatasetToStorage(datasetKey) {
+ if (!this.canUseLocalStorage()) {
+ return
+ }
+
+ const dataset = this.datasets[datasetKey]
+
+ if (!dataset?.rawText) {
+ window.localStorage.removeItem(this.storageKey(datasetKey))
+ if (datasetKey === this.selectedDatasetKey) {
+ window.localStorage.removeItem(this.activeDatasetStorageKey())
+ }
+ return
+ }
+
+ const payload = JSON.stringify({
+ rawText: dataset.rawText,
+ snapshotText: dataset.snapshotText,
+ sourceLabel: dataset.sourceLabel || ''
+ })
+
+ window.localStorage.setItem(this.storageKey(datasetKey), payload)
+
+ if (datasetKey === this.selectedDatasetKey) {
+ window.localStorage.setItem(
+ this.activeDatasetStorageKey(),
+ JSON.stringify({
+ datasetKey,
+ rawText: dataset.rawText,
+ snapshotText: dataset.snapshotText,
+ sourceLabel: dataset.sourceLabel || ''
+ })
+ )
+ }
+ },
+ restoreActiveDatasetFromStorage() {
+ if (!this.canUseLocalStorage()) {
+ return false
+ }
+
+ const storedValue = window.localStorage.getItem(this.activeDatasetStorageKey())
+ if (!storedValue) {
+ return false
+ }
+
+ const parsedStoredValue = parseJsonText(storedValue)
+ if (!parsedStoredValue.ok || !isPlainObject(parsedStoredValue.data)) {
+ window.localStorage.removeItem(this.activeDatasetStorageKey())
+ return false
+ }
+
+ const { datasetKey } = parsedStoredValue.data
+
+ if (
+ typeof datasetKey !== 'string' ||
+ !DATASET_DEFS.some(def => def.key === datasetKey)
+ ) {
+ window.localStorage.removeItem(this.activeDatasetStorageKey())
+ return false
+ }
+
+ const rawText = String(parsedStoredValue.data.rawText || '').trim()
+ const snapshotText =
+ typeof parsedStoredValue.data.snapshotText === 'string'
+ ? parsedStoredValue.data.snapshotText
+ : rawText
+
+ if (!rawText) {
+ window.localStorage.removeItem(this.activeDatasetStorageKey())
+ return false
+ }
+
+ const parsedDataset = parseJsonText(rawText)
+ if (!parsedDataset.ok) {
+ window.localStorage.removeItem(this.activeDatasetStorageKey())
+ return false
+ }
+
+ this.selectedDatasetKey = datasetKey
+ this.commitDataset(datasetKey, parsedDataset.data, {
+ rawText,
+ snapshotText,
+ sourceLabel:
+ parsedStoredValue.data.sourceLabel ||
+ `Local storage: ${this.datasets[datasetKey].fileName}`,
+ fileHandle: null,
+ loadError: ''
+ })
+
+ return true
+ },
+ restoreDatasetFromStorage(datasetKey) {
+ if (!this.canUseLocalStorage()) {
+ return false
+ }
+
+ const storedValue = window.localStorage.getItem(this.storageKey(datasetKey))
+ if (!storedValue) {
+ return false
+ }
+
+ const parsedStoredValue = parseJsonText(storedValue)
+ if (!parsedStoredValue.ok || !isPlainObject(parsedStoredValue.data)) {
+ window.localStorage.removeItem(this.storageKey(datasetKey))
+ return false
+ }
+
+ const rawText = String(parsedStoredValue.data.rawText || '').trim()
+ const snapshotText =
+ typeof parsedStoredValue.data.snapshotText === 'string'
+ ? parsedStoredValue.data.snapshotText
+ : rawText
+
+ if (!rawText) {
+ window.localStorage.removeItem(this.storageKey(datasetKey))
+ return false
+ }
+
+ const parsedDataset = parseJsonText(rawText)
+ if (!parsedDataset.ok) {
+ window.localStorage.removeItem(this.storageKey(datasetKey))
+ return false
+ }
+
+ this.commitDataset(datasetKey, parsedDataset.data, {
+ rawText,
+ snapshotText,
+ sourceLabel:
+ parsedStoredValue.data.sourceLabel ||
+ `Local storage: ${this.datasets[datasetKey].fileName}`,
+ fileHandle: null,
+ loadError: ''
+ })
+
+ return true
+ },
+ persistUiState() {
+ if (!this.canUseLocalStorage()) {
+ return
+ }
+
+ window.localStorage.setItem(
+ this.uiStateStorageKey(),
+ JSON.stringify({
+ selectedDatasetKey: this.selectedDatasetKey,
+ selectedFunctionByDataset: this.selectedFunctionByDataset,
+ selectedFundingSourceByDataset: this.selectedFundingSourceByDataset,
+ selectedTestFundingSourceByDataset:
+ this.selectedTestFundingSourceByDataset,
+ selectedTestTypeByScope: this.selectedTestTypeByScope
+ })
+ )
+ },
+ restoreUiState() {
+ if (!this.canUseLocalStorage()) {
+ return
+ }
+
+ const storedValue = window.localStorage.getItem(this.uiStateStorageKey())
+ if (!storedValue) {
+ return
+ }
+
+ const parsedStoredValue = parseJsonText(storedValue)
+ if (!parsedStoredValue.ok || !isPlainObject(parsedStoredValue.data)) {
+ window.localStorage.removeItem(this.uiStateStorageKey())
+ return
+ }
+
+ const state = parsedStoredValue.data
+
+ if (
+ typeof state.selectedDatasetKey === 'string' &&
+ DATASET_DEFS.some(def => def.key === state.selectedDatasetKey)
+ ) {
+ this.selectedDatasetKey = state.selectedDatasetKey
+ }
+
+ if (isPlainObject(state.selectedFunctionByDataset)) {
+ Object.assign(this.selectedFunctionByDataset, state.selectedFunctionByDataset)
+ }
+
+ if (isPlainObject(state.selectedFundingSourceByDataset)) {
+ Object.assign(
+ this.selectedFundingSourceByDataset,
+ state.selectedFundingSourceByDataset
+ )
+ }
+
+ if (isPlainObject(state.selectedTestFundingSourceByDataset)) {
+ Object.assign(
+ this.selectedTestFundingSourceByDataset,
+ state.selectedTestFundingSourceByDataset
+ )
+ }
+
+ if (isPlainObject(state.selectedTestTypeByScope)) {
+ this.selectedTestTypeByScope = {
+ ...state.selectedTestTypeByScope
+ }
+ }
+ },
notify(message, type = 'info', caption = '') {
Quasar.Notify.create({
message,
@@ -3416,7 +3656,7 @@
return parsed.data
},
- persistTestCallParams(testEntry) {
+ persistTestCallParams(testEntry, options = {}) {
if (!this.currentFunctionName) {
return
}
@@ -3432,7 +3672,13 @@
} catch (error) {
testEntry.callParamsError =
error instanceof Error ? error.message : String(error)
- this.notify('Could not save call params.', 'negative', testEntry.callParamsError)
+ if (!options.silent) {
+ this.notify(
+ 'Could not save call params.',
+ 'negative',
+ testEntry.callParamsError
+ )
+ }
return
}
@@ -3448,7 +3694,7 @@
}
)
},
- persistTestExpected(testEntry) {
+ persistTestExpected(testEntry, options = {}) {
if (!this.currentFunctionName) {
return
}
@@ -3464,11 +3710,13 @@
} catch (error) {
testEntry.expectedError =
error instanceof Error ? error.message : String(error)
- this.notify(
- `Could not save ${testEntry.expectedLabel.toLowerCase()}.`,
- 'negative',
- testEntry.expectedError
- )
+ if (!options.silent) {
+ this.notify(
+ `Could not save ${testEntry.expectedLabel.toLowerCase()}.`,
+ 'negative',
+ testEntry.expectedError
+ )
+ }
return
}
@@ -3484,7 +3732,7 @@
}
)
},
- persistTestMockSet(testEntry, mockSet) {
+ persistTestMockSet(testEntry, mockSet, options = {}) {
if (!this.currentFunctionName || !this.currentTestFundingSourceName) {
return
}
@@ -3506,11 +3754,13 @@
if (!mockName) {
if ((binding.items || []).length) {
mockSet.error = 'Select a function mock before editing its overrides.'
- this.notify(
- 'Could not save test mock definition.',
- 'negative',
- mockSet.error
- )
+ if (!options.silent) {
+ this.notify(
+ 'Could not save test mock definition.',
+ 'negative',
+ mockSet.error
+ )
+ }
return
}
@@ -3519,11 +3769,13 @@
if (Object.prototype.hasOwnProperty.call(nextValue, mockName)) {
mockSet.error = `Duplicate function mock selected: ${mockName}`
- this.notify(
- 'Could not save test mock definition.',
- 'negative',
- mockSet.error
- )
+ if (!options.silent) {
+ this.notify(
+ 'Could not save test mock definition.',
+ 'negative',
+ mockSet.error
+ )
+ }
return
}
@@ -3555,11 +3807,13 @@
item.requestBodyError =
error instanceof Error ? error.message : String(error)
mockSet.error = item.requestBodyError
- this.notify(
- 'Could not save test mock definition.',
- 'negative',
- mockSet.error
- )
+ if (!options.silent) {
+ this.notify(
+ 'Could not save test mock definition.',
+ 'negative',
+ mockSet.error
+ )
+ }
return
}
}
@@ -3578,11 +3832,13 @@
item.responseError =
error instanceof Error ? error.message : String(error)
mockSet.error = item.responseError
- this.notify(
- 'Could not save test mock definition.',
- 'negative',
- mockSet.error
- )
+ if (!options.silent) {
+ this.notify(
+ 'Could not save test mock definition.',
+ 'negative',
+ mockSet.error
+ )
+ }
return
}
}
@@ -3675,7 +3931,7 @@
rows.splice(rowIndex, 1)
this.persistFundingSourceSettings()
},
- persistFundingSourceSettings() {
+ persistFundingSourceSettings(options = {}) {
if (!this.currentFundingSourceName) {
return
}
@@ -3699,11 +3955,13 @@
} catch (error) {
row.error =
error instanceof Error ? error.message : String(error)
- this.notify(
- `Could not save setting "${key || 'new setting'}".`,
- 'negative',
- row.error
- )
+ if (!options.silent) {
+ this.notify(
+ `Could not save setting "${key || 'new setting'}".`,
+ 'negative',
+ row.error
+ )
+ }
return
}
}
@@ -3736,7 +3994,7 @@
rows.splice(rowIndex, 1)
this.persistFunctionMocks()
},
- persistFunctionMocks() {
+ persistFunctionMocks(options = {}) {
if (!this.currentFunctionName || !this.currentTestFundingSourceName) {
return
}
@@ -3760,11 +4018,13 @@
} catch (error) {
row.error =
error instanceof Error ? error.message : String(error)
- this.notify(
- `Could not save mock "${name || 'new mock'}".`,
- 'negative',
- row.error
- )
+ if (!options.silent) {
+ this.notify(
+ `Could not save mock "${name || 'new mock'}".`,
+ 'negative',
+ row.error
+ )
+ }
return
}
}
@@ -3890,6 +4150,31 @@
)
}
}
+
+ this.persistDatasetToStorage(datasetKey)
+ this.persistUiState()
+ },
+ syncEditorFromRawText(datasetKey, options = {}) {
+ const dataset = this.datasets[datasetKey]
+ const parsed = parseJsonText(dataset.rawText)
+
+ if (!parsed.ok) {
+ dataset.parseError = `Invalid JSON in ${dataset.fileName}: ${parsed.error}`
+ if (!options.silent) {
+ this.notify('Could not apply the editor contents.', 'negative')
+ }
+ return false
+ }
+
+ this.commitDataset(datasetKey, parsed.data, {
+ rawText: dataset.rawText,
+ snapshotText: dataset.snapshotText,
+ fileHandle: dataset.fileHandle,
+ sourceLabel: dataset.sourceLabel,
+ loadError: dataset.loadError
+ })
+
+ return true
},
validateEditor(datasetKey) {
const dataset = this.datasets[datasetKey]