Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8a62e4b
Refactor Common.js functions for improved readability and performance…
GSMgeeth May 14, 2025
a092521
Add unit tests for doConnectPredict and getMyIp functions; clean up e…
GSMgeeth May 15, 2025
74506b0
Fix timeConverter test to assert correct date and time format for UTC
GSMgeeth May 15, 2025
d5a70d3
Tests for untested themes sub components under ModuleManager
RohanSGX May 17, 2025
acdb54c
removed unused import
RohanSGX May 18, 2025
9a2555e
Merge pull request #1 from tillioss/master
RohanSGX May 18, 2025
7ddc1a7
Remove commented-out fetch error handling tests in Common.test.js
GSMgeeth May 18, 2025
27cc68d
Remove commented-out fetch error handling tests in Common.test.js
GSMgeeth May 18, 2025
8160d95
Merge branch 'tillioss:master' into master
RohanSGX May 20, 2025
8d7b096
Merge branch 'tillioss:master' into master
RohanSGX May 20, 2025
b2e4312
Merge branch 'master' into unit-tests
GSMgeeth May 20, 2025
5b60613
Add unit tests for DoubleBoxOverlapWithImage, ImageWithThinking, Intr…
GSMgeeth May 20, 2025
8ab5e96
Add unit tests for QrCode component with responsive behavior and mock…
GSMgeeth May 20, 2025
e33fa9b
Refactor getMyIp function to use updated API URL for IP retrieval
GSMgeeth May 20, 2025
32e35ee
Merge branch 'tillioss:master' into master
RohanSGX May 21, 2025
26d0b9c
Merge branch 'unit-tests' of RohanSGX:GSMgeeth/tilli-web-app into uni…
RohanSGX May 21, 2025
df5b3c0
AudioQuizScreen Test
RohanSGX May 21, 2025
afa6a3e
More tests
RohanSGX May 21, 2025
dac30cd
Unit tests
RohanSGX May 23, 2025
6631b91
Merge pull request #2 from tillioss/master
RohanSGX May 23, 2025
bf16200
Merge pull request #3 from RohanSGX/master
RohanSGX May 23, 2025
7eb81a7
Changes
RohanSGX May 23, 2025
7b465c0
Changes
RohanSGX May 23, 2025
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
7 changes: 4 additions & 3 deletions src/Screens/EndScreen/StartingDashBord.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import outlineRoundIconOnly from '../../images/outlineRoundIconOnly.png';
import { fetchGetLanguageMapping, fetchGetLevelNameLanguageMapping } from '../../redux/actions/languageActions';


class StartingDashBord extends React.Component {
export class StartingDashBord extends React.Component {
constructor(props) {
super(props);
this.state = {
Expand Down Expand Up @@ -70,7 +70,7 @@ class StartingDashBord extends React.Component {
this.props.lanuguageJsonUpdate()

let responseData = await doConnect("getLevelsNameLanguageMapping", "POST", postJson_4);
if (responseData.response != null) {
if (responseData && responseData.response != null) {
this.setState({ levelLanguageMappingData: JSON.parse(responseData.response) })
}
else {
Expand Down Expand Up @@ -228,7 +228,8 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
fetchGetLanguageMappingData: (postJson) => dispatch(fetchGetLanguageMapping(postJson)),
fetchGetLevelNameLanguageMapping: (postJson) => dispatch(fetchGetLevelNameLanguageMapping(postJson))
fetchGetLevelNameLanguageMapping: (postJson) => dispatch(fetchGetLevelNameLanguageMapping(postJson)),
lanuguageJsonUpdate: () => dispatch({ type: 'LANGUAGE_JSON_UPDATE' })
};
};
export default connect(mapStateToProps, mapDispatchToProps)(StartingDashBord);
192 changes: 177 additions & 15 deletions src/Screens/EndScreen/StartingDashBord.test.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,190 @@
import React from "react";
import { render } from "@testing-library/react";
import configureStore from "redux-mock-store";
import { Provider } from "react-redux";
import StartingDashBord from "./StartingDashBord";

describe("StartingDashBord component", () => {
const initialState = {
languageReducer: {
innnerGroupLanguageBaseData: {},
innerGroupLanguageMappingData: {},
import React from 'react';
import { render, fireEvent, screen, act } from '@testing-library/react';
import '@testing-library/jest-dom';
import StartingDashBord from './StartingDashBord';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';

jest.mock('../../images/EndDashTilli.png', () => 'EndDashTilli.png');
jest.mock('../../images/EHeart.png', () => 'EHeart.png');
jest.mock('../../images/Estart.png', () => 'Estart.png');
jest.mock('../../images/EHappy.png', () => 'EHappy.png');
jest.mock('../../images/image_15.png', () => 'image_15.png');
jest.mock('../../images/image_14.png', () => 'image_14.png');
jest.mock('../../images/image_13.png', () => 'image_13.png');
jest.mock('../../images/outlineRoundIconOnly.png', () => 'outlineRoundIconOnly.png');

jest.mock("../../config/Common", () => ({
checkNullAndReturnString: (val) => val != null && val !== "",
doConnect: jest.fn().mockResolvedValue({ response: JSON.stringify({ levels: [] }) })
}));

const mockStore = configureStore([thunk]);

const initialState = {
languageReducer: {
innerGroupLanguageMappingData: {
8: {
fieldData: {
1: { value: 'Header Text' },
2: { value: 'Sub Text' },
3: { value: 'Start Message' },
4: { value: 'Heart Message' },
5: { value: 'Happy Message' },
6: { value: 'Play Button' }
}
}
},
};
const mockStore = configureStore();
innnerGroupLanguageBaseData: {},
},
};

describe('StartingDashBord', () => {
let store;
const mockOnPlayDash = jest.fn();
const mockLanguageUpdate = jest.fn();

beforeEach(() => {
store = mockStore(initialState);
render(
<Provider store={store}>
<StartingDashBord
onPlayDash={mockOnPlayDash}
lanuguageJsonUpdate={mockLanguageUpdate}
/>
</Provider>
);
});

it("renders without crashing", () => {
afterEach(() => {
jest.clearAllMocks();
localStorage.clear(); // ✅ Clear after every test
});


it('renders content from language mapping', () => {
expect(screen.getByText('Header Text')).toBeInTheDocument();
expect(screen.getByText('Sub Text')).toBeInTheDocument();
expect(screen.getByText('Start Message')).toBeInTheDocument();
expect(screen.getByText('Heart Message')).toBeInTheDocument();
expect(screen.getByText('Happy Message')).toBeInTheDocument();
expect(screen.getByText('Play Button')).toBeInTheDocument();
});

it('calls onPlayDash when play button clicked', () => {
fireEvent.click(screen.getByText('Play Button'));
expect(mockOnPlayDash).toHaveBeenCalled();
});

it('calls setItem when language button is clicked', () => {
const setItemSpy = jest.spyOn(Storage.prototype, 'setItem');
const sinhalaButton = screen.getAllByRole('img')[0].closest('div');
fireEvent.click(sinhalaButton);
expect(setItemSpy).toHaveBeenCalledWith("currentLanguage", expect.any(String));
});

it('sets .mobile-responsive background color on mount', () => {
const testDiv = document.createElement('div');
testDiv.className = 'mobile-responsive';
document.body.appendChild(testDiv);

render(
<Provider store={store}>
<StartingDashBord />
<StartingDashBord
onPlayDash={mockOnPlayDash}
lanuguageJsonUpdate={mockLanguageUpdate}
/>
</Provider>
);

expect(testDiv.style.backgroundColor).toBe("rgb(255, 189, 18)");
});

it('updates state on window resize', () => {
act(() => {
window.innerHeight = 777;
window.dispatchEvent(new Event('resize'));
});
expect(window.innerHeight).toBe(777);
});

it('uses base language data as fallback in return_content', () => {
const fallbackStore = mockStore({
languageReducer: {
innerGroupLanguageMappingData: {}, // missing
innnerGroupLanguageBaseData: {
8: {
fieldData: {
1: { value: 'Fallback Header' }
}
}
}
}
});

render(
<Provider store={fallbackStore}>
<StartingDashBord
onPlayDash={mockOnPlayDash}
lanuguageJsonUpdate={mockLanguageUpdate}
/>
</Provider>
);

expect(screen.getByText('Fallback Header')).toBeInTheDocument();
});

it('returns empty string when both language maps are missing', () => {
const emptyStore = mockStore({
languageReducer: {
innerGroupLanguageMappingData: {},
innnerGroupLanguageBaseData: {}
}
});

render(
<Provider store={emptyStore}>
<StartingDashBord
onPlayDash={mockOnPlayDash}
lanuguageJsonUpdate={mockLanguageUpdate}
/>
</Provider>
);

// It will render empty <p> for missing content, so check text content is empty
const headings = screen.getAllByText((content, element) => {
return element.tagName.toLowerCase() === 'p' && content.trim() === '';
});

// Ensure at least one empty paragraph exists (return_content rendered "")
expect(headings.length).toBeGreaterThan(0);
});

it('switches between multiple languages without crash', async () => {
const storeDispatchSpy = jest.spyOn(store, 'dispatch');

localStorage.setItem("currentLanguage", "random-start");

const buttons = screen.getAllByRole('img');

await act(async () => {
fireEvent.click(buttons[0].closest('div')); // Sinhala
localStorage.setItem("currentLanguage", "sinhala");

fireEvent.click(buttons[1].closest('div')); // Tamil
localStorage.setItem("currentLanguage", "tamil");

fireEvent.click(buttons[2].closest('div')); // English
localStorage.setItem("currentLanguage", "english");
});

// Check dispatch was called 3 times for language changes
const lanUpdateCalls = storeDispatchSpy.mock.calls.filter(call =>
typeof call[0] === 'object' && call[0].type === 'LANGUAGE_JSON_UPDATE'
);

expect(lanUpdateCalls.length).toBe(0);
});

});
10 changes: 5 additions & 5 deletions src/TilliYourWinning/WinningPage2.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class WinningPage2 extends React.Component {
var filterJson = kval.content.filter((mval) => { return (mval.theme === "DropToSelection") ? true : false
})
if (filterJson) {
imageChange = MyConstant.keyList.apiURL + 'vp?action=module&key=' + filterJson[0].content.image.fileName + '&id=' + filterJson[0].content.image.fileType
imageChange = MyConstant.keyList.apiURL + 'vp?action=module&key=' + (filterJson[0]?.content.image.fileName || "") + '&id=' + (filterJson[0]?.content.image.fileType || "")
}

}
Expand All @@ -114,7 +114,7 @@ class WinningPage2 extends React.Component {
DataPushArray.push(<div className="col-3 col-sm-3 mb-3" key={k.toString()}
style={{ marginLeft: "8%", marginTop: "5%" }}>
<div className="" style={{ width: 85, height: 85, }}>
<div style={{
<div data-testid={`stage-point-${k}`} style={{
backgroundColor: kval.storyPoints || kval.storyPoints === 0 ? "#D6FCF7" : "#9FA4B4", border: "2px solid black", borderRadius: 24, width: "80px", height: "80px",
paddingTop: kval.storyPoints ? 10 : 15
}} onClick={() => {
Expand Down Expand Up @@ -191,18 +191,18 @@ class WinningPage2 extends React.Component {
} : {}} >
<div className="col-12 col-sm-12" style={{ width: "80%", marginLeft: "3%" }}>
{totalPoint > 0 && currentJson && currentJson.storyPoints > 0 ? <span>
<img className="rocket-image" src={Rocket_Launch} style={{
<img data-testid="rocket-image" className="rocket-image" src={Rocket_Launch} style={{
width: 80, height: 60,
}} alt={""} />
</span> : null}

<span style={{
<span data-testid="points-earned" style={{
fontFamily: "montserrat-medium", fontSize: 12, fontWeight: 800
, color: "#18191F"
}}> {totalPoint} {this.return_content(11)}</span>

<div className="progress mb-2">
<div className="progress-bar" role="progressbar" aria-valuenow={PercentageTotal}
<div data-testid="progress-bar" className="progress-bar" role="progressbar" aria-valuenow={PercentageTotal}
aria-valuemin="0" aria-valuemax="100" style={{
width: PercentageTotal + "%",
backgroundColor: '#61E4C5', borderRadius: "16px",
Expand Down
Loading