Merge pull request #55 from djwesty/djwesty/40
Improve Chip Distribution (Issue #40)
This commit is contained in:
commit
292cd7b797
3
app.json
3
app.json
@ -9,7 +9,8 @@
|
|||||||
"userInterfaceStyle": "automatic",
|
"userInterfaceStyle": "automatic",
|
||||||
"newArchEnabled": true,
|
"newArchEnabled": true,
|
||||||
"ios": {
|
"ios": {
|
||||||
"supportsTablet": true
|
"supportsTablet": true,
|
||||||
|
"bundleIdentifier": "com.anonymous.pokerchipshelper"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"adaptiveIcon": {
|
"adaptiveIcon": {
|
||||||
|
@ -10,6 +10,14 @@ interface BuyInSelectorProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const defaultBuyInOptions = [10, 25, 50];
|
const defaultBuyInOptions = [10, 25, 50];
|
||||||
|
const MIN = 1;
|
||||||
|
const MAX = 200;
|
||||||
|
|
||||||
|
const parseRoundClamp = (num: string): number => {
|
||||||
|
const parsed = parseFloat(num);
|
||||||
|
const rounded = Math.round(parsed);
|
||||||
|
return Math.min(Math.max(rounded, MIN), MAX);
|
||||||
|
};
|
||||||
|
|
||||||
const BuyInSelector: React.FC<BuyInSelectorProps> = ({
|
const BuyInSelector: React.FC<BuyInSelectorProps> = ({
|
||||||
setBuyInAmount,
|
setBuyInAmount,
|
||||||
@ -19,9 +27,9 @@ const BuyInSelector: React.FC<BuyInSelectorProps> = ({
|
|||||||
const [buyInAmount, setBuyInAmountState] = useState<number | null>(null);
|
const [buyInAmount, setBuyInAmountState] = useState<number | null>(null);
|
||||||
|
|
||||||
const handleCustomAmountChange = (value: string) => {
|
const handleCustomAmountChange = (value: string) => {
|
||||||
const numericValue = parseFloat(value);
|
const numericValue = parseRoundClamp(value);
|
||||||
if (!isNaN(numericValue) && numericValue >= 0) {
|
if (!isNaN(numericValue) && numericValue >= 0) {
|
||||||
setCustomAmount(value);
|
setCustomAmount(numericValue.toString());
|
||||||
setBuyInAmountState(numericValue);
|
setBuyInAmountState(numericValue);
|
||||||
setBuyInAmount(numericValue);
|
setBuyInAmount(numericValue);
|
||||||
} else {
|
} else {
|
||||||
@ -50,18 +58,17 @@ const BuyInSelector: React.FC<BuyInSelectorProps> = ({
|
|||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text style={styles.p}>{i18n.t("custom_buy_in")}</Text>
|
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
value={customAmount}
|
value={customAmount}
|
||||||
|
maxLength={3}
|
||||||
onChangeText={handleCustomAmountChange}
|
onChangeText={handleCustomAmountChange}
|
||||||
placeholder={i18n.t("enter_custom_buy_in")}
|
placeholder={`${i18n.t("custom_buy_in")} ${MIN} - ${MAX}`}
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text style={styles.h2}>
|
<Text style={styles.h2}>
|
||||||
{`${i18n.t("selected_buy_in")}: `}
|
{`${i18n.t("selected_buy_in")} `}
|
||||||
{buyInAmount !== null
|
{buyInAmount !== null
|
||||||
? `${selectedCurrency} ${buyInAmount}`
|
? `${selectedCurrency} ${buyInAmount}`
|
||||||
: i18n.t("none")}
|
: i18n.t("none")}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { View, Text, StyleSheet } from "react-native";
|
import { View, Text, Alert } from "react-native";
|
||||||
import { ColorValue } from "react-native";
|
import { ColorValue } from "react-native";
|
||||||
import i18n from "@/i18n/i18n";
|
import i18n from "@/i18n/i18n";
|
||||||
import styles from "@/styles/styles";
|
import styles, { COLORS } from "@/styles/styles";
|
||||||
|
import { MaterialIcons } from "@expo/vector-icons";
|
||||||
|
|
||||||
interface ChipDistributionSummaryProps {
|
interface ChipDistributionSummaryProps {
|
||||||
playerCount: number;
|
playerCount: number;
|
||||||
@ -12,6 +13,8 @@ interface ChipDistributionSummaryProps {
|
|||||||
selectedCurrency: string;
|
selectedCurrency: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reverseFib: number[] = [8, 5, 3, 2, 1];
|
||||||
|
|
||||||
const ChipDistributionSummary = ({
|
const ChipDistributionSummary = ({
|
||||||
playerCount,
|
playerCount,
|
||||||
buyInAmount,
|
buyInAmount,
|
||||||
@ -20,11 +23,15 @@ const ChipDistributionSummary = ({
|
|||||||
selectedCurrency = "$",
|
selectedCurrency = "$",
|
||||||
}: ChipDistributionSummaryProps) => {
|
}: ChipDistributionSummaryProps) => {
|
||||||
const validDenominations: validDenomination[] = [
|
const validDenominations: validDenomination[] = [
|
||||||
0.05, 0.1, 0.25, 0.5, 1, 2, 2.5, 5, 10, 20, 50, 100,
|
0.05, 0.1, 0.25, 1, 5, 10, 20, 50, 100,
|
||||||
];
|
];
|
||||||
const [denominations, setDenominations] = useState<validDenomination[]>([]);
|
const [denominations, setDenominations] = useState<validDenomination[]>([]);
|
||||||
const [distributions, setDistributions] = useState<number[]>([]);
|
const [distributions, setDistributions] = useState<number[]>([]);
|
||||||
|
|
||||||
|
const showAlert = () => {
|
||||||
|
Alert.alert(i18n.t("warning"), i18n.t("chip_value_warn"));
|
||||||
|
};
|
||||||
|
|
||||||
type validDenomination =
|
type validDenomination =
|
||||||
| 0.05
|
| 0.05
|
||||||
| 0.1
|
| 0.1
|
||||||
@ -36,120 +43,112 @@ const ChipDistributionSummary = ({
|
|||||||
| 5
|
| 5
|
||||||
| 10
|
| 10
|
||||||
| 20
|
| 20
|
||||||
|
| 25
|
||||||
| 50
|
| 50
|
||||||
| 100;
|
| 100;
|
||||||
|
|
||||||
const findFloorDenomination = (target: number): validDenomination => {
|
const findFloorDenomination = (target: number): validDenomination => {
|
||||||
let current: validDenomination = validDenominations[0];
|
let current: validDenomination = validDenominations[0];
|
||||||
validDenominations.forEach((value, index) => {
|
validDenominations.forEach((value, _) => {
|
||||||
if (value < target) current = value;
|
if (value < target) current = value;
|
||||||
});
|
});
|
||||||
return current;
|
return current;
|
||||||
};
|
};
|
||||||
|
|
||||||
const maxDenomination = useMemo(() => {
|
const round = useCallback((num: number) => Math.round(num * 100) / 100, []);
|
||||||
if (totalChipsCount.length > 3) {
|
|
||||||
return findFloorDenomination(buyInAmount / 3);
|
// Bound for the value of the highest chip
|
||||||
} else {
|
// This is somewhat arbitray and imperfect, but 1/3 to 1/5 is reasonable depending on the number of colors.
|
||||||
return findFloorDenomination(buyInAmount / 4);
|
// Could be possibly improved based on value of buy in amount
|
||||||
|
const maxDenomination: validDenomination = useMemo(() => {
|
||||||
|
let max: validDenomination;
|
||||||
|
switch (totalChipsCount.length) {
|
||||||
|
case 5:
|
||||||
|
case 4:
|
||||||
|
max = findFloorDenomination(buyInAmount / 3);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
max = findFloorDenomination(buyInAmount / 4);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 1:
|
||||||
|
default:
|
||||||
|
max = findFloorDenomination(buyInAmount / 5);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}, [totalChipsCount]);
|
return max;
|
||||||
|
}, [totalChipsCount, buyInAmount]);
|
||||||
|
|
||||||
const potValue = useMemo(
|
const potValue = useMemo(
|
||||||
() => buyInAmount * playerCount,
|
() => buyInAmount * playerCount,
|
||||||
[buyInAmount, playerCount]
|
[buyInAmount, playerCount]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// The total value of all chips distributed to a single player. Ideally should be equal to buyInAmount
|
||||||
const totalValue = useMemo(() => {
|
const totalValue = useMemo(() => {
|
||||||
let value = 0;
|
let value = 0;
|
||||||
for (let i = 0; i < totalChipsCount.length; i++) {
|
for (let i = 0; i < distributions.length; i++) {
|
||||||
value += distributions[i] * denominations[i];
|
value += distributions[i] * denominations[i];
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}, [distributions, denominations]);
|
}, [distributions, denominations]);
|
||||||
|
|
||||||
|
// Maximum quantity of each chip color which may be distributed to a single player before running out
|
||||||
const maxPossibleDistribution = useMemo(
|
const maxPossibleDistribution = useMemo(
|
||||||
() => totalChipsCount.map((v) => Math.floor(v / playerCount)),
|
() => totalChipsCount.map((v) => Math.floor(v / playerCount)),
|
||||||
[totalChipsCount, playerCount]
|
[totalChipsCount, playerCount]
|
||||||
);
|
);
|
||||||
|
|
||||||
const redenominate = useCallback(
|
// Dynamically set denominations and distributions from changing inputs
|
||||||
(
|
|
||||||
invalidDenomination: validDenomination[],
|
|
||||||
shuffleIndex: number
|
|
||||||
): validDenomination[] => {
|
|
||||||
let moved = false;
|
|
||||||
const newDenomination: validDenomination[] = [];
|
|
||||||
for (let i = invalidDenomination.length - 1; i >= 0; i--) {
|
|
||||||
if (i > shuffleIndex) {
|
|
||||||
newDenomination.push(invalidDenomination[i]);
|
|
||||||
} else if (i == shuffleIndex) {
|
|
||||||
newDenomination.push(invalidDenomination[i]);
|
|
||||||
} else if (i < shuffleIndex && !moved) {
|
|
||||||
const nextLowestDenominationIndex = Math.max(
|
|
||||||
validDenominations.indexOf(invalidDenomination[i]) - 1,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
newDenomination.push(validDenominations[nextLowestDenominationIndex]);
|
|
||||||
moved = true;
|
|
||||||
} else {
|
|
||||||
newDenomination.push(invalidDenomination[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newDenomination.reverse();
|
|
||||||
return newDenomination;
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let testDenomination: validDenomination[] = [];
|
let testDenomination: validDenomination[] = [];
|
||||||
const numColors = totalChipsCount.length;
|
const totalNumColors = totalChipsCount.length;
|
||||||
const testDistribution: number[] = [];
|
|
||||||
for (let i = 0; i < numColors; ++i) {
|
|
||||||
testDistribution.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Start with max denominations, then push on the next adjacent lower denomination
|
||||||
testDenomination.push(maxDenomination);
|
testDenomination.push(maxDenomination);
|
||||||
let currentDenominationIndex: number =
|
let currentDenominationIndex: number =
|
||||||
validDenominations.indexOf(maxDenomination);
|
validDenominations.indexOf(maxDenomination);
|
||||||
for (let i = numColors - 2; i >= 0; i = i - 1) {
|
for (
|
||||||
|
let i = totalNumColors - 2;
|
||||||
|
i >= 0 && currentDenominationIndex > 0;
|
||||||
|
i = i - 1
|
||||||
|
) {
|
||||||
currentDenominationIndex -= 1;
|
currentDenominationIndex -= 1;
|
||||||
const currentDemoniation = validDenominations[currentDenominationIndex];
|
const currentDemoniation = validDenominations[currentDenominationIndex];
|
||||||
testDenomination.push(currentDemoniation);
|
testDenomination.push(currentDemoniation);
|
||||||
}
|
}
|
||||||
testDenomination.reverse();
|
testDenomination.reverse();
|
||||||
|
let numColors = testDenomination.length;
|
||||||
|
|
||||||
let remainingValue = buyInAmount;
|
const testDistribution: number[] = [];
|
||||||
let fail = true;
|
for (let i = 0; i < numColors; ++i) {
|
||||||
let failCount = 0;
|
testDistribution.push(0);
|
||||||
while (fail && failCount < 1) {
|
|
||||||
let stop = false;
|
|
||||||
while (remainingValue > 0 && !stop) {
|
|
||||||
let distributed = false;
|
|
||||||
for (let i = numColors - 1; i >= 0; i = i - 1) {
|
|
||||||
if (testDistribution[i] < maxPossibleDistribution[i]) {
|
|
||||||
if (remainingValue >= testDenomination[i]) {
|
|
||||||
testDistribution[i] = testDistribution[i] + 1;
|
|
||||||
remainingValue = remainingValue - testDenomination[i];
|
|
||||||
distributed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (distributed == false) {
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (remainingValue !== 0) {
|
|
||||||
const redenominateIndex = failCount % numColors;
|
|
||||||
testDenomination = redenominate(testDenomination, redenominateIndex);
|
|
||||||
failCount += 1;
|
|
||||||
fail = true;
|
|
||||||
} else {
|
|
||||||
fail = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Distribute the chips using the test denomination with a reverse fibbonaci preference
|
||||||
|
// Not optimal, nor correct under all inputs but works for most inputs
|
||||||
|
// Algorithm could be improved with more complexity and optimization (re-tries, redenominating, etc.)
|
||||||
|
let remainingValue = buyInAmount;
|
||||||
|
let stop = false;
|
||||||
|
while (remainingValue > 0 && !stop) {
|
||||||
|
let distributed = false;
|
||||||
|
for (let i = numColors - 1; i >= 0; i = i - 1) {
|
||||||
|
for (
|
||||||
|
let j = reverseFib[i];
|
||||||
|
j > 0 &&
|
||||||
|
remainingValue >= testDenomination[i] &&
|
||||||
|
testDistribution[i] < maxPossibleDistribution[i];
|
||||||
|
j = j - 1
|
||||||
|
) {
|
||||||
|
testDistribution[i] = testDistribution[i] + 1;
|
||||||
|
remainingValue = round(remainingValue - testDenomination[i]);
|
||||||
|
distributed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (distributed == false) {
|
||||||
|
stop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
setDenominations(testDenomination);
|
setDenominations(testDenomination);
|
||||||
setDistributions(testDistribution);
|
setDistributions(testDistribution);
|
||||||
}, [totalChipsCount, maxDenomination, buyInAmount, playerCount]);
|
}, [totalChipsCount, maxDenomination, buyInAmount, playerCount]);
|
||||||
@ -157,24 +156,39 @@ const ChipDistributionSummary = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{totalChipsCount.map((_, index) => (
|
{distributions.map((distribution, index) => {
|
||||||
<View style={{ flexDirection: "row" }} key={index}>
|
return (
|
||||||
<Text
|
distribution > 0 && (
|
||||||
style={{
|
<View style={{ flexDirection: "row" }} key={index}>
|
||||||
...styles.h2,
|
<Text
|
||||||
color: colors[index],
|
style={{
|
||||||
...(colors[index] === "white" && styles.shadow),
|
...styles.h2,
|
||||||
}}
|
fontWeight: "bold",
|
||||||
>
|
color: colors[index],
|
||||||
{`${distributions[index]} ${i18n.t("chips")}: ${selectedCurrency}${denominations[index]} ${i18n.t("each")}`}
|
...(colors[index] === "white" && styles.shadow),
|
||||||
</Text>
|
}}
|
||||||
</View>
|
>
|
||||||
))}
|
{`${distribution} ${i18n.t("chips")}: ${selectedCurrency}${denominations[index]} ${i18n.t("each")}`}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})}
|
||||||
</View>
|
</View>
|
||||||
<View style={{ flexDirection: "row", justifyContent: "space-between" }}>
|
<View style={{ flexDirection: "row", justifyContent: "space-between" }}>
|
||||||
<Text style={styles.p}>
|
<View style={[styles.container, { flexDirection: "row", gap: 1 }]}>
|
||||||
{i18n.t("total_value")}: {selectedCurrency} {totalValue}
|
<Text style={styles.p}>
|
||||||
</Text>
|
{i18n.t("total_value")}: {selectedCurrency} {round(totalValue)}{" "}
|
||||||
|
</Text>
|
||||||
|
{round(totalValue) !== buyInAmount && (
|
||||||
|
<MaterialIcons
|
||||||
|
name="warning"
|
||||||
|
size={20}
|
||||||
|
color={COLORS.WARNING}
|
||||||
|
onPress={showAlert}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
<Text style={styles.p}>
|
<Text style={styles.p}>
|
||||||
{selectedCurrency} {potValue} {i18n.t("pot")}
|
{selectedCurrency} {potValue} {i18n.t("pot")}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -14,6 +14,7 @@ import styles from "@/styles/styles";
|
|||||||
import i18n from "@/i18n/i18n";
|
import i18n from "@/i18n/i18n";
|
||||||
|
|
||||||
const colors: ColorValue[] = ["white", "red", "green", "blue", "black"];
|
const colors: ColorValue[] = ["white", "red", "green", "blue", "black"];
|
||||||
|
const defaults = [100, 50, 50, 50, 50];
|
||||||
|
|
||||||
const ChipInputModal = ({
|
const ChipInputModal = ({
|
||||||
showModal,
|
showModal,
|
||||||
@ -152,7 +153,7 @@ const ChipsSelector = ({
|
|||||||
} else if (numberOfChips > totalChipsCount.length) {
|
} else if (numberOfChips > totalChipsCount.length) {
|
||||||
for (let colorIndex = 0; colorIndex < numberOfChips; ++colorIndex) {
|
for (let colorIndex = 0; colorIndex < numberOfChips; ++colorIndex) {
|
||||||
if (colorIndex >= newTotalChipsCount.length) {
|
if (colorIndex >= newTotalChipsCount.length) {
|
||||||
const defaultTotal = 100 - colorIndex * 20;
|
const defaultTotal = defaults[colorIndex];
|
||||||
newTotalChipsCount.push(defaultTotal);
|
newTotalChipsCount.push(defaultTotal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,9 @@ describe("BuyInSelector Component", () => {
|
|||||||
expect(getByText("$ 10")).toBeTruthy();
|
expect(getByText("$ 10")).toBeTruthy();
|
||||||
expect(getByText("$ 25")).toBeTruthy();
|
expect(getByText("$ 25")).toBeTruthy();
|
||||||
expect(getByText("$ 50")).toBeTruthy();
|
expect(getByText("$ 50")).toBeTruthy();
|
||||||
expect(getByPlaceholderText("Enter custom buy-in")).toBeTruthy();
|
expect(
|
||||||
|
getByPlaceholderText("Or, enter a custom amount: 1 - 200")
|
||||||
|
).toBeTruthy();
|
||||||
expect(queryByText(/Selected Buy-in:.*None/i)).toBeTruthy();
|
expect(queryByText(/Selected Buy-in:.*None/i)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -53,24 +55,36 @@ describe("BuyInSelector Component", () => {
|
|||||||
it("sets a custom buy-in amount correctly", () => {
|
it("sets a custom buy-in amount correctly", () => {
|
||||||
const { getByPlaceholderText } = renderComponent();
|
const { getByPlaceholderText } = renderComponent();
|
||||||
|
|
||||||
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "100");
|
fireEvent.changeText(
|
||||||
|
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
|
||||||
|
"100"
|
||||||
|
);
|
||||||
expect(setBuyInAmount).toHaveBeenCalledWith(100);
|
expect(setBuyInAmount).toHaveBeenCalledWith(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("resets custom amount if invalid input is entered", () => {
|
it("bound and validate custom amount if invalid input is entered", () => {
|
||||||
const { getByPlaceholderText } = renderComponent();
|
const { getByPlaceholderText } = renderComponent();
|
||||||
|
|
||||||
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "-10");
|
fireEvent.changeText(
|
||||||
expect(setBuyInAmount).toHaveBeenCalledWith(25); // Default reset
|
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
|
||||||
|
"-10"
|
||||||
|
);
|
||||||
|
expect(setBuyInAmount).toHaveBeenCalledWith(1); // Min value
|
||||||
|
|
||||||
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "abc");
|
fireEvent.changeText(
|
||||||
expect(setBuyInAmount).toHaveBeenCalledWith(25);
|
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
|
||||||
|
"abc"
|
||||||
|
);
|
||||||
|
expect(setBuyInAmount).toHaveBeenCalledWith(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("clears the custom amount when selecting a predefined option", () => {
|
it("clears the custom amount when selecting a predefined option", () => {
|
||||||
const { getByPlaceholderText, getByText } = renderComponent();
|
const { getByPlaceholderText, getByText } = renderComponent();
|
||||||
|
|
||||||
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "100");
|
fireEvent.changeText(
|
||||||
|
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
|
||||||
|
"100"
|
||||||
|
);
|
||||||
fireEvent.press(getByText("$ 50"));
|
fireEvent.press(getByText("$ 50"));
|
||||||
expect(setBuyInAmount).toHaveBeenCalledWith(50);
|
expect(setBuyInAmount).toHaveBeenCalledWith(50);
|
||||||
});
|
});
|
||||||
@ -78,13 +92,22 @@ describe("BuyInSelector Component", () => {
|
|||||||
it("handles valid and invalid input for custom amount correctly", () => {
|
it("handles valid and invalid input for custom amount correctly", () => {
|
||||||
const { getByPlaceholderText } = renderComponent();
|
const { getByPlaceholderText } = renderComponent();
|
||||||
|
|
||||||
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "75");
|
fireEvent.changeText(
|
||||||
|
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
|
||||||
|
"75"
|
||||||
|
);
|
||||||
expect(setBuyInAmount).toHaveBeenCalledWith(75);
|
expect(setBuyInAmount).toHaveBeenCalledWith(75);
|
||||||
|
|
||||||
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "-5");
|
fireEvent.changeText(
|
||||||
expect(setBuyInAmount).toHaveBeenCalledWith(25);
|
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
|
||||||
|
"-5"
|
||||||
|
);
|
||||||
|
expect(setBuyInAmount).toHaveBeenCalledWith(1);
|
||||||
|
|
||||||
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "abc");
|
fireEvent.changeText(
|
||||||
|
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
|
||||||
|
"abc"
|
||||||
|
);
|
||||||
expect(setBuyInAmount).toHaveBeenCalledWith(25);
|
expect(setBuyInAmount).toHaveBeenCalledWith(25);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -98,7 +121,7 @@ describe("BuyInSelector Component", () => {
|
|||||||
|
|
||||||
it("resets to default buy-in when custom input is cleared", () => {
|
it("resets to default buy-in when custom input is cleared", () => {
|
||||||
const { getByPlaceholderText } = renderComponent();
|
const { getByPlaceholderText } = renderComponent();
|
||||||
const input = getByPlaceholderText("Enter custom buy-in");
|
const input = getByPlaceholderText("Or, enter a custom amount: 1 - 200");
|
||||||
|
|
||||||
fireEvent.changeText(input, "75");
|
fireEvent.changeText(input, "75");
|
||||||
expect(setBuyInAmount).toHaveBeenCalledWith(75);
|
expect(setBuyInAmount).toHaveBeenCalledWith(75);
|
||||||
@ -110,7 +133,10 @@ describe("BuyInSelector Component", () => {
|
|||||||
it("updates state correctly when selecting predefined buy-in after entering a custom amount", () => {
|
it("updates state correctly when selecting predefined buy-in after entering a custom amount", () => {
|
||||||
const { getByPlaceholderText, getByText } = renderComponent();
|
const { getByPlaceholderText, getByText } = renderComponent();
|
||||||
|
|
||||||
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "200");
|
fireEvent.changeText(
|
||||||
|
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
|
||||||
|
"200"
|
||||||
|
);
|
||||||
expect(setBuyInAmount).toHaveBeenCalledWith(200);
|
expect(setBuyInAmount).toHaveBeenCalledWith(200);
|
||||||
|
|
||||||
fireEvent.press(getByText("$ 10"));
|
fireEvent.press(getByText("$ 10"));
|
||||||
|
@ -2,13 +2,20 @@ import React from "react";
|
|||||||
import { render } from "@testing-library/react-native";
|
import { render } from "@testing-library/react-native";
|
||||||
import ChipDistributionSummary from "../ChipDistributionSummary";
|
import ChipDistributionSummary from "../ChipDistributionSummary";
|
||||||
|
|
||||||
|
jest.mock("@expo/vector-icons", () => {
|
||||||
|
const { Text } = require("react-native");
|
||||||
|
return {
|
||||||
|
MaterialIcons: () => <Text>TestIcon</Text>,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
describe("ChipDistributionSummary Component", () => {
|
describe("ChipDistributionSummary Component", () => {
|
||||||
test("renders correctly with valid data", () => {
|
test("renders correctly with valid data", () => {
|
||||||
const playerCount = 4;
|
const playerCount = 4;
|
||||||
const totalChipsCount = [100, 80, 60, 40, 20];
|
const totalChipsCount = [100, 80, 60, 40, 20];
|
||||||
const buyInAmount = 20;
|
const buyInAmount = 20;
|
||||||
const expectedDistribution = [2, 2, 1, 2, 2];
|
const expectedDistribution = [16, 12, 8, 6, 2];
|
||||||
const expectedDenominations = [0.5, 1, 2, 2.5, 5];
|
const expectedDenominations = [0.05, 0.1, 0.25, 1, 5];
|
||||||
|
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<ChipDistributionSummary
|
<ChipDistributionSummary
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
"inr": "INR (₹)",
|
"inr": "INR (₹)",
|
||||||
"select_number_of_players": "Select the Number of Players:",
|
"select_number_of_players": "Select the Number of Players:",
|
||||||
"select_buyin_amount": "Select Buy-in Amount:",
|
"select_buyin_amount": "Select Buy-in Amount:",
|
||||||
"custom_buy_in": "Or enter a custom amount:",
|
"custom_buy_in": "Or, enter a custom amount:",
|
||||||
"enter_custom_buy_in": "Enter custom buy-in",
|
|
||||||
"selected_buy_in": "Selected Buy-in:",
|
"selected_buy_in": "Selected Buy-in:",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"pick_an_image": "Pick an image",
|
"pick_an_image": "Pick an image",
|
||||||
@ -37,6 +36,7 @@
|
|||||||
"failed_to_save_state": "Failed to save state.",
|
"failed_to_save_state": "Failed to save state.",
|
||||||
"state_loaded_from": "State loaded from",
|
"state_loaded_from": "State loaded from",
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
|
"warning": "Warning",
|
||||||
"no_saved_state_found": "No saved state found.",
|
"no_saved_state_found": "No saved state found.",
|
||||||
"automatic_chip_detection": "Automatic Chip Detection",
|
"automatic_chip_detection": "Automatic Chip Detection",
|
||||||
"manual_chip_adjustment": "Manual Chip Adjustment",
|
"manual_chip_adjustment": "Manual Chip Adjustment",
|
||||||
@ -45,6 +45,7 @@
|
|||||||
"save_slot_2": "Save\nSlot 2",
|
"save_slot_2": "Save\nSlot 2",
|
||||||
"load_slot_1": "Load\nSlot 1",
|
"load_slot_1": "Load\nSlot 1",
|
||||||
"load_slot_2": "Load\nSlot 2",
|
"load_slot_2": "Load\nSlot 2",
|
||||||
"please_select_valid_buyin": "Please select a valid buy-in amount"
|
"please_select_valid_buyin": "Please select a valid buy-in amount",
|
||||||
|
"chip_value_warn": "Be advised that the value of the distributed chips does not equal the buy-in for these inputs.\n\nHowever, results shown are fair to all players"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
"inr": "INR (₹)",
|
"inr": "INR (₹)",
|
||||||
"select_number_of_players": "Seleccionar número de jugadores:",
|
"select_number_of_players": "Seleccionar número de jugadores:",
|
||||||
"select_buyin_amount": "Seleccionar cantidad de buy-in:",
|
"select_buyin_amount": "Seleccionar cantidad de buy-in:",
|
||||||
"custom_buy_in": "O ingresa una cantidad personalizada:",
|
"custom_buy_in": "O, ingresa una cantidad personalizada:",
|
||||||
"enter_custom_buy_in": "Ingresar buy-in personalizado",
|
|
||||||
"selected_buy_in": "Buy-in seleccionado:",
|
"selected_buy_in": "Buy-in seleccionado:",
|
||||||
"none": "Ninguno",
|
"none": "Ninguno",
|
||||||
"pick_an_image": "Elige una imagen",
|
"pick_an_image": "Elige una imagen",
|
||||||
@ -38,6 +37,7 @@
|
|||||||
"failed_to_save_state": "No se pudo guardar el estado.",
|
"failed_to_save_state": "No se pudo guardar el estado.",
|
||||||
"state_loaded_from": "Estado cargado desde",
|
"state_loaded_from": "Estado cargado desde",
|
||||||
"info": "Información",
|
"info": "Información",
|
||||||
|
"warning": "Advertencia",
|
||||||
"no_saved_state_found": "No se encontró estado guardado.",
|
"no_saved_state_found": "No se encontró estado guardado.",
|
||||||
"automatic_chip_detection": "Detección automática de fichas",
|
"automatic_chip_detection": "Detección automática de fichas",
|
||||||
"manual_chip_adjustment": "Ajuste manual de fichas",
|
"manual_chip_adjustment": "Ajuste manual de fichas",
|
||||||
@ -46,6 +46,7 @@
|
|||||||
"save_slot_2": "Guardar\nSlot 2",
|
"save_slot_2": "Guardar\nSlot 2",
|
||||||
"load_slot_1": "Cargar\nSlot 1",
|
"load_slot_1": "Cargar\nSlot 1",
|
||||||
"load_slot_2": "Cargar\nSlot 2",
|
"load_slot_2": "Cargar\nSlot 2",
|
||||||
"please_select_valid_buyin": "Por favor seleccione una cantidad de buy-in válida"
|
"please_select_valid_buyin": "Por favor seleccione una cantidad de buy-in válida",
|
||||||
|
"chip_value_warn": "Tenga en cuenta que el valor de las fichas distribuidas no es igual al buy-in para estas entradas.\n\nSin embargo, los resultados que se muestran son justos para todos los jugadores."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ export const COLORS = {
|
|||||||
SECONDARY: "#6c757d",
|
SECONDARY: "#6c757d",
|
||||||
SUCCESS: "#28a745",
|
SUCCESS: "#28a745",
|
||||||
DANGER: "#dc3545",
|
DANGER: "#dc3545",
|
||||||
WARNING: "#ffc107",
|
WARNING: "#c79c28",
|
||||||
};
|
};
|
||||||
|
|
||||||
const lightStyles = StyleSheet.create({});
|
const lightStyles = StyleSheet.create({});
|
||||||
|
Loading…
Reference in New Issue
Block a user