Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions tests/unit/getCurrentReceiptStateTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import getCurrentReceiptState from '@pages/iou/request/step/confirmation/submission/utils/getCurrentReceiptState';

import CONST from '@src/CONST';
import type {Receipt} from '@src/types/onyx/Transaction';
import type Transaction from '@src/types/onyx/Transaction';

import createRandomTransaction from '../utils/collections/transaction';

const TRANSACTION_ID = '1';

const RECEIPT: Receipt = {source: 'file://receipt.jpg'};

function buildTransaction(overrides: Partial<Transaction> = {}): Transaction {
return {
...createRandomTransaction(Number(TRANSACTION_ID)),
iouRequestType: CONST.IOU.REQUEST_TYPE.SCAN,
isAmountSet: false,
isMerchantSet: false,
isCreatedSet: false,
...overrides,
};
}

describe('getCurrentReceiptState', () => {
it('returns undefined when the transaction has no receipt file', () => {
// Given a scan transaction whose ID has no entry in the receipt files
const item = buildTransaction();

// When the receipt state is resolved
const result = getCurrentReceiptState({item, receiptFiles: {}, canEnterScanFieldsManually: true});

// Then no state is set, since there is no receipt to submit
expect(result).toBeUndefined();
});

it('returns undefined when the surface does not offer the manual scan fields', () => {
// Given a scan transaction with a receipt, on a surface that never showed the amount / merchant / date fields
const item = buildTransaction({isAmountSet: true, isMerchantSet: true, isCreatedSet: true});

// When the receipt state is resolved
const result = getCurrentReceiptState({item, receiptFiles: {[TRANSACTION_ID]: RECEIPT}, canEnterScanFieldsManually: false});

// Then no state is set, so the flags carried by the transaction are ignored
expect(result).toBeUndefined();
});

it('returns undefined for a test receipt', () => {
// Given a scan transaction whose receipt is a test receipt
const item = buildTransaction();

// When the receipt state is resolved
const result = getCurrentReceiptState({item, receiptFiles: {[TRANSACTION_ID]: {...RECEIPT, isTestReceipt: true}}, canEnterScanFieldsManually: true});

// Then no state is set, because test receipts are not scanned
expect(result).toBeUndefined();
});

it('returns undefined for a test drive receipt', () => {
// Given a scan transaction whose receipt is a test drive receipt
const item = buildTransaction();

// When the receipt state is resolved
const result = getCurrentReceiptState({item, receiptFiles: {[TRANSACTION_ID]: {...RECEIPT, isTestDriveReceipt: true}}, canEnterScanFieldsManually: true});

// Then no state is set, because test drive receipts are not scanned
expect(result).toBeUndefined();
});

it('returns undefined when the transaction is not a scan request', () => {
// Given a manual transaction that has a receipt attached
const item = buildTransaction({iouRequestType: CONST.IOU.REQUEST_TYPE.MANUAL});

// When the receipt state is resolved
const result = getCurrentReceiptState({item, receiptFiles: {[TRANSACTION_ID]: RECEIPT}, canEnterScanFieldsManually: true});

// Then no state is set, since only scan requests decide between scanning and manual entry
expect(result).toBeUndefined();
});

it('returns SCAN_READY when none of the manual scan fields are filled in', () => {
// Given a scan transaction with a receipt and no amount, merchant or date entered
const item = buildTransaction();

// When the receipt state is resolved
const result = getCurrentReceiptState({item, receiptFiles: {[TRANSACTION_ID]: RECEIPT}, canEnterScanFieldsManually: true});

// Then the receipt is sent to SmartScan
expect(result).toBe(CONST.IOU.RECEIPT_STATE.SCAN_READY);
});

it('returns SCAN_READY when only some of the manual scan fields are filled in', () => {
// Given a scan transaction with a receipt where only the amount was entered
const item = buildTransaction({isAmountSet: true});

// When the receipt state is resolved
const result = getCurrentReceiptState({item, receiptFiles: {[TRANSACTION_ID]: RECEIPT}, canEnterScanFieldsManually: true});

// Then the receipt is still sent to SmartScan, because the fields are all-or-nothing
expect(result).toBe(CONST.IOU.RECEIPT_STATE.SCAN_READY);
});

it('returns OPEN when all manual scan fields are filled in', () => {
// Given a scan transaction with a receipt where amount, merchant and date were all entered
const item = buildTransaction({isAmountSet: true, isMerchantSet: true, isCreatedSet: true});

// When the receipt state is resolved
const result = getCurrentReceiptState({item, receiptFiles: {[TRANSACTION_ID]: RECEIPT}, canEnterScanFieldsManually: true});

// Then the expense is submitted as manual, so SmartScan never overwrites the entered values
expect(result).toBe(CONST.IOU.RECEIPT_STATE.OPEN);
});
});
79 changes: 79 additions & 0 deletions tests/unit/getSelectedParticipantsForSubmissionTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import getSelectedParticipantsForSubmission from '@pages/iou/request/step/confirmation/submission/utils/getSelectedParticipantsForSubmission';

import CONST from '@src/CONST';
import type {Participant} from '@src/types/onyx/IOU';
import type {SplitShares} from '@src/types/onyx/Transaction';
import type Transaction from '@src/types/onyx/Transaction';

import createRandomTransaction from '../utils/collections/transaction';

const ALICE_ACCOUNT_ID = 1;
const BOB_ACCOUNT_ID = 2;
const WORKSPACE_OWNER_ACCOUNT_ID = 3;

const ALICE: Participant = {accountID: ALICE_ACCOUNT_ID, login: 'alice@test.com'};
const BOB: Participant = {accountID: BOB_ACCOUNT_ID, login: 'bob@test.com'};
const WORKSPACE_CHAT: Participant = {reportID: '100', isPolicyExpenseChat: true, ownerAccountID: WORKSPACE_OWNER_ACCOUNT_ID};

function buildTransaction(splitShares?: SplitShares): Transaction {
return {...createRandomTransaction(1), splitShares};
}

describe('getSelectedParticipantsForSubmission', () => {
it('returns the selected participants untouched for a non-split expense', () => {
// Given a transaction where Bob's amount is zero
const transaction = buildTransaction({[ALICE_ACCOUNT_ID]: {amount: 100}, [BOB_ACCOUNT_ID]: {amount: 0}});
const selectedParticipants = [ALICE, BOB];

// When the participants are picked for a non-split (submit) expense
const result = getSelectedParticipantsForSubmission({transaction, iouType: CONST.IOU.TYPE.SUBMIT, selectedParticipants});

// Then split shares are ignored and the selection is returned as-is, because shares only apply to splits
expect(result).toBe(selectedParticipants);
});

it('returns the selected participants untouched for a split without split shares', () => {
// Given a split whose transaction carries no split shares yet
const transaction = buildTransaction();
const selectedParticipants = [ALICE, BOB];

// When the participants for submission are resolved
const result = getSelectedParticipantsForSubmission({transaction, iouType: CONST.IOU.TYPE.SPLIT, selectedParticipants});

// Then nobody is filtered out, since there are no amounts to filter by
expect(result).toBe(selectedParticipants);
});

it('drops split participants whose share is zero', () => {
// Given a split where Alice owes an amount and Bob's share was set to zero
const transaction = buildTransaction({[ALICE_ACCOUNT_ID]: {amount: 100}, [BOB_ACCOUNT_ID]: {amount: 0}});

// When the participants for submission are resolved
const result = getSelectedParticipantsForSubmission({transaction, iouType: CONST.IOU.TYPE.SPLIT, selectedParticipants: [ALICE, BOB]});

// Then only Alice is kept, so no zero-amount split is created for Bob
expect(result).toEqual([ALICE]);
});

it('drops split participants that have no split share entry', () => {
// Given a split whose shares only include Alice
const transaction = buildTransaction({[ALICE_ACCOUNT_ID]: {amount: 100}});

// When the participants are picked for a split
const result = getSelectedParticipantsForSubmission({transaction, iouType: CONST.IOU.TYPE.SPLIT, selectedParticipants: [ALICE, BOB]});

// Then Bob is dropped, because only participants with a positive share are kept
expect(result).toEqual([ALICE]);
});

it('matches a workspace chat participant by its owner account ID', () => {
// Given a split with a workspace chat whose owner has a non-zero share, keyed by the owner's account ID
const transaction = buildTransaction({[ALICE_ACCOUNT_ID]: {amount: 0}, [WORKSPACE_OWNER_ACCOUNT_ID]: {amount: 100}});

// When the participants for submission are resolved
const result = getSelectedParticipantsForSubmission({transaction, iouType: CONST.IOU.TYPE.SPLIT, selectedParticipants: [ALICE, WORKSPACE_CHAT]});

// Then the workspace chat is kept through its owner's share and Alice is dropped
expect(result).toEqual([WORKSPACE_CHAT]);
});
});
Loading
Loading