From 4dd42dfe757876e9d287d9a6923a59c95dd6d904 Mon Sep 17 00:00:00 2001 From: Willy Bruns Date: Fri, 7 Oct 2016 04:37:36 -0700 Subject: [PATCH 1/2] Payment History modal receipt filenames are distinct when multiple transactions occur in same day Receipt filenames for the Nth transaction in a given day have a suffix `_N` added when N > 1 e.g. for two transactions occur on 10/7/2016, the 2nd filename will be `Brave_Payments_10-7-2016_2.csv` fixes #4605 --- js/about/preferences.js | 14 +++++++------- js/lib/ledgerExportUtil.js | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/js/about/preferences.js b/js/about/preferences.js index 40643ca6124..2d923edff70 100644 --- a/js/about/preferences.js +++ b/js/about/preferences.js @@ -9,7 +9,9 @@ const Immutable = require('immutable') const SwitchControl = require('../components/switchControl') const ModalOverlay = require('../components/modalOverlay') const cx = require('../lib/classSet') -const transactionsToCSVDataURL = require('../lib/ledgerExportUtil').transactionsToCSVDataURL +const ledgerExportUtil = require('../lib/ledgerExportUtil') +const transactionsToCSVDataURL = ledgerExportUtil.transactionsToCSVDataURL +const addExportFilenamePrefixToTransactions = ledgerExportUtil.addExportFilenamePrefixToTransactions const {getZoomValuePercentage} = require('../lib/zoom') const config = require('../constants/config') const appConfig = require('../constants/appConfig') @@ -465,7 +467,9 @@ class PaymentHistory extends ImmutableComponent { } render () { - const transactions = this.props.ledgerData.get('transactions') + const transactions = Immutable.fromJS( + addExportFilenamePrefixToTransactions(this.props.ledgerData.get('transactions').toJS()) + ) return
@@ -502,10 +506,6 @@ class PaymentHistoryRow extends ImmutableComponent { return formattedDateFromTimestamp(this.timestamp) } - get numericDateStr () { - return (new Date(this.timestamp)).toLocaleDateString().replace(/\//g, '-') - } - get ledgerData () { return this.props.ledgerData } @@ -528,7 +528,7 @@ class PaymentHistoryRow extends ImmutableComponent { } get receiptFileName () { - return `Brave_Payments_${this.numericDateStr}.csv` + return `${this.transaction.get('exportFilenamePrefix')}.csv` } get dataURL () { diff --git a/js/lib/ledgerExportUtil.js b/js/lib/ledgerExportUtil.js index 6625b7206e1..ecfddfc3d22 100644 --- a/js/lib/ledgerExportUtil.js +++ b/js/lib/ledgerExportUtil.js @@ -279,11 +279,46 @@ let getTransactionCSVText = function (transactions, viewingIds, addTotalRow) { return getTransactionCSVRows(transactions, viewingIds, addTotalRow).join('\n') } +/** + * Adds an `exportFilenamePrefix` field to the provided transaction(s) + * of form `Brave_Payments_${MM-D(D)-YYYY}`, with "_" added for the nth time a date occurs (n > 1) + * + * @param {Object[]} transactions - an array of transaction(s) or single transaction object + */ +let addExportFilenamePrefixToTransactions = function (transactions) { + if (!transactions) { + return transactions + } + + if (!underscore.isArray(transactions)) { + transactions = [transactions] + } + + var dateCountMap = {} + + return transactions.map(function (transaction) { + let timestamp = transaction.submissionStamp + let numericDateStr = (new Date(timestamp)).toLocaleDateString().replace(/\//g, '-') + + let dateCount = (dateCountMap[numericDateStr] ? dateCountMap[numericDateStr] : 1) + dateCountMap[numericDateStr] = dateCount + 1 + + if (dateCount > 1) { + numericDateStr = `${numericDateStr}_${dateCount}` + } + + transaction.exportFilenamePrefix = `Brave_Payments_${numericDateStr}` + + return transaction + }) +} + module.exports = { transactionsToCSVDataURL, getTransactionCSVText, getTransactionCSVRows, getPublisherVoteData, getTransactionsByViewingIds, - getTotalContribution + getTotalContribution, + addExportFilenamePrefixToTransactions } From 40d6a018d9ed6c310051095cf610fc0eaef4cbb7 Mon Sep 17 00:00:00 2001 From: Willy Bruns Date: Sat, 8 Oct 2016 07:16:34 -0700 Subject: [PATCH 2/2] Add tests for ledgerExportUtil.addExportFilenamePrefixToTransactions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tests for #4605 (https://github.com/brave/browser-laptop/issues/4605) ``` addExportFilenamePrefixToTransactions ✓ should return an empty array if not passed any transactions (empty array, null, or undefined input ✓ should return the same output for a single transaction given as an array or single object ✓ should add a field "exportFilenamePrefix" to each transaction with correct form ("Brave_Payments_${MM)-YYYY}") ✓ should add a distinct suffix ("_") to "exportFilenamePrefix" when multiple transactions occur on same day to ensure the field value is unique ``` --- js/lib/ledgerExportUtil.js | 10 ++- test/unit/lib/ledgerExportUtilTest.js | 88 +++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/js/lib/ledgerExportUtil.js b/js/lib/ledgerExportUtil.js index ecfddfc3d22..6de77c56dc9 100644 --- a/js/lib/ledgerExportUtil.js +++ b/js/lib/ledgerExportUtil.js @@ -284,16 +284,20 @@ let getTransactionCSVText = function (transactions, viewingIds, addTotalRow) { * of form `Brave_Payments_${MM-D(D)-YYYY}`, with "_" added for the nth time a date occurs (n > 1) * * @param {Object[]} transactions - an array of transaction(s) or single transaction object + * + * @returns {Object[]} transactions (with each element having an added field `exportFilenamePrefix`) */ let addExportFilenamePrefixToTransactions = function (transactions) { - if (!transactions) { - return transactions - } + transactions = transactions || [] if (!underscore.isArray(transactions)) { transactions = [transactions] } + if (!transactions.length) { + return transactions + } + var dateCountMap = {} return transactions.map(function (transaction) { diff --git a/test/unit/lib/ledgerExportUtilTest.js b/test/unit/lib/ledgerExportUtilTest.js index 644d19b0ba2..c3f10bd7cf1 100644 --- a/test/unit/lib/ledgerExportUtilTest.js +++ b/test/unit/lib/ledgerExportUtilTest.js @@ -1,6 +1,7 @@ /* global describe, it, before */ const assert = require('assert') const underscore = require('underscore') +const uuid = require('node-uuid') require('../braveUnit') @@ -20,6 +21,9 @@ const CSV_CONTENT_TYPE = 'text/csv' const CSV_DATA_URI_PREFIX = 'data:' + CSV_CONTENT_TYPE + ';base64,' const EMPTY_CSV_DATA_URL = CSV_DATA_URI_PREFIX + base64Encode(EMPTY_CSV) +const EXPORT_FILENAME_CONST_PREFIX_PART = 'Brave_Payments_' +const EXPORT_FILENAME_PREFIX_EXPECTED_FORM = `${EXPORT_FILENAME_CONST_PREFIX_PART}\${MM-D(D)-YYYY}` + const exampleTransactions = require('./exampleLedgerData').transactions const exampleTransaction = exampleTransactions[0] @@ -381,8 +385,92 @@ describe('ledger export utilities test', function () { }) }) }) + + describe('addExportFilenamePrefixToTransactions', function () { + it('should return an empty array if not passed any transactions (empty array, null, or undefined input)', function () { + let output + + output = ledgerExportUtil.addExportFilenamePrefixToTransactions([]) + assert(output && underscore.isArray(output) && output.length === 0, 'should return an empty array when given an empty array as input') + + output = ledgerExportUtil.addExportFilenamePrefixToTransactions(undefined) + assert(output && underscore.isArray(output) && output.length === 0, 'should return an empty array when given undefined as input') + + output = ledgerExportUtil.addExportFilenamePrefixToTransactions(null) + assert(output && underscore.isArray(output) && output.length === 0, 'should return an empty array when given null as input') + }) + + it('should return the same output for a single transaction given as an array or single object', function () { + let txs = [cloneTransactionWithNewViewingId(exampleTransaction)] + assert(txs[0] && !txs[0].exportFilenamePrefix, 'the example transaction should not already have "exportFilenamePrefix" defined') + + let outputFromArray = ledgerExportUtil.addExportFilenamePrefixToTransactions(txs) + let outputFromObject = ledgerExportUtil.addExportFilenamePrefixToTransactions(txs[0]) + + assert.deepEqual(outputFromArray, outputFromObject, 'the same output should be returned for an array with 1 transaction and the transaction object itself') + }) + + it(`should add a field "exportFilenamePrefix" to each transaction with correct form ("${EXPORT_FILENAME_PREFIX_EXPECTED_FORM}")`, function () { + let txs = [cloneTransactionWithNewViewingId(exampleTransaction)] + assert(txs[0] && !txs[0].exportFilenamePrefix, 'the example transaction should not already have "exportFilenamePrefix" defined') + txs = ledgerExportUtil.addExportFilenamePrefixToTransactions(txs) + + let tx = txs[0] + let timestamp = tx.submissionStamp + let dateStr = (new Date(timestamp)).toLocaleDateString().replace(/\//g, '-') + let expectedExportFilenamePrefix = `${EXPORT_FILENAME_CONST_PREFIX_PART}${dateStr}` + + assert.equal(typeof tx.exportFilenamePrefix, 'string', 'transaction should have "exportFilenamePrefix" field with type "string"') + assert.equal(tx.exportFilenamePrefix, expectedExportFilenamePrefix, `"exportFilenamePrefix" field should have expected form: "${EXPORT_FILENAME_PREFIX_EXPECTED_FORM}", here with date string = "${dateStr}"`) + }) + + it('should add a distinct suffix ("_") to "exportFilenamePrefix" when multiple transactions occur on same day to ensure the field value is unique', function () { + // create 3 clone transactions identical except for viewingId + // -> these will all have a submissionStamp corresponding to the same DAY + let txs = [cloneTransactionWithNewViewingId(exampleTransaction), cloneTransactionWithNewViewingId(exampleTransaction), cloneTransactionWithNewViewingId(exampleTransaction)] + + let sameDaySubmissionStamp = txs[0].submissionStamp + + // add one more clone transaction but modify the date to be a day later + // -> this should NOT get the distinguishing suffix + let txOnDifferentDate = cloneTransactionWithNewViewingId(exampleTransaction) + txOnDifferentDate.submissionStamp += 1000 * 3600 * 48 // shift by 48 hours (2 days) + txs.push(txOnDifferentDate) + + txs.forEach(function (tx) { + assert(tx && !tx.exportFilenamePrefix, 'the example transactions should not already have "exportFilenamePrefix" defined') + }) + + txs = ledgerExportUtil.addExportFilenamePrefixToTransactions(txs) + + let numSameDayTxProcessed = 0 + txs.forEach(function (tx, idx) { + assert.equal(typeof tx.exportFilenamePrefix, 'string', 'each transactions should now have a "exportFilenamePrefix" field of type "string" defined') + + if (tx.submissionStamp === sameDaySubmissionStamp) { + numSameDayTxProcessed++ + } + + let firstTransactionForDate = !idx || tx.submissionStamp !== sameDaySubmissionStamp + if (firstTransactionForDate) { + let errMessage = `the first transaction for a given date should NOT have the distinguishing "_" suffix: "${tx.exportFilenamePrefix}" (tx idx = ${idx})` + assert.equal(tx.exportFilenamePrefix.slice(tx.exportFilenamePrefix.length - 2).indexOf('_'), -1, errMessage) + } else { // if 2nd or 3rd transaction on a given date... + assert.equal(tx.exportFilenamePrefix.slice(tx.exportFilenamePrefix.length - 2), `_${numSameDayTxProcessed}`, 'the second and third transaction for a given date SHOULD have the suffix "_"') + } + }) + }) + }) }) +// clone a transaction but give it a unique viewingId +function cloneTransactionWithNewViewingId (tx) { + let cloneTx = underscore.clone(tx) + cloneTx.viewingId = uuid.v4().toLowerCase() + + return cloneTx +} + function checkColumnCountsForRows (rows) { for (var rowIdx = 0; rowIdx < rows.length; rowIdx++) { let row = rows[rowIdx]