compileprofile-spacetests 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CLAUDE.md +114 -114
  2. package/README.md +171 -147
  3. package/dist/cjs/common-interfaces.d.ts +33 -1
  4. package/dist/cjs/gonogo.js +89 -57
  5. package/dist/cjs/nback.d.ts +6 -2
  6. package/dist/cjs/nback.js +132 -2
  7. package/dist/cjs/spatial-cognition.js +0 -2
  8. package/dist/esm/common-interfaces.d.ts +33 -1
  9. package/dist/esm/gonogo.js +89 -57
  10. package/dist/esm/nback.d.ts +6 -2
  11. package/dist/esm/nback.js +128 -2
  12. package/dist/esm/spatial-cognition.js +0 -2
  13. package/example-data/emotions.json +807 -807
  14. package/example-data/gonogo.json +5392 -5392
  15. package/example-data/nback.json +892 -892
  16. package/example-data/participant.json +6 -6
  17. package/example-data/spatial-cognition.json +462 -462
  18. package/jest.config.js +13 -13
  19. package/original/emotion-recognition.R +39 -39
  20. package/original/gonogo.R +32 -32
  21. package/package.json +40 -40
  22. package/src/__tests__/integration/browserInfo.test.ts +16 -16
  23. package/src/__tests__/integration/emotions.test.ts +172 -172
  24. package/src/__tests__/integration/gonogo.test.ts +293 -183
  25. package/src/__tests__/integration/nback-versions.test.ts +182 -0
  26. package/src/__tests__/integration/nback.test.ts +148 -148
  27. package/src/__tests__/integration/spatial-cognition.test.ts +171 -171
  28. package/src/__tests__/test-utils/testData.ts +79 -79
  29. package/src/common-interfaces.ts +124 -78
  30. package/src/emotions.ts +98 -98
  31. package/src/getters.ts +21 -21
  32. package/src/gonogo.ts +149 -127
  33. package/src/index.ts +39 -39
  34. package/src/nback.ts +182 -61
  35. package/src/spatial-cognition.ts +110 -112
  36. package/tsconfig.cjs.json +12 -12
  37. package/tsconfig.esm.json +14 -14
package/jest.config.js CHANGED
@@ -1,14 +1,14 @@
1
- module.exports = {
2
- preset: 'ts-jest',
3
- testEnvironment: 'node',
4
- roots: ['<rootDir>/src'],
5
- testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
6
- transform: {
7
- '^.+\\.ts$': 'ts-jest'
8
- },
9
- moduleFileExtensions: ['ts', 'js', 'json', 'node'],
10
- // ignore test-utils folder
11
- testPathIgnorePatterns: ['<rootDir>/src/__tests__/test-utils/'],
12
- setupFilesAfterEnv: ['<rootDir>/src/__tests__/test-utils/testData.ts'],
13
- moduleDirectories: ['node_modules', 'example-data']
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ roots: ['<rootDir>/src'],
5
+ testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
6
+ transform: {
7
+ '^.+\\.ts$': 'ts-jest'
8
+ },
9
+ moduleFileExtensions: ['ts', 'js', 'json', 'node'],
10
+ // ignore test-utils folder
11
+ testPathIgnorePatterns: ['<rootDir>/src/__tests__/test-utils/'],
12
+ setupFilesAfterEnv: ['<rootDir>/src/__tests__/test-utils/testData.ts'],
13
+ moduleDirectories: ['node_modules', 'example-data']
14
14
  };
@@ -1,39 +1,39 @@
1
- emotion_create_trial_table <- function(txt) {
2
- emotion_values <- c(`0` = "neutral", `1` = "sad", `2` = "happy",
3
- `3` = "surprised", `4` = "angry")
4
- df <- results_to_df(txt) %>%
5
- emotions_extract_emotion() %>%
6
- filter(!is.na(response), !is.na(emotion)) %>%
7
- mutate(emotion_response = emotion_values[response + 1],
8
- correct = emotion == emotion_response,
9
- index = 1:n(),
10
- block = c(rep("regular", 15), rep("quick", 15), rep("rotated", 15))) %>%
11
- select(emotion, emotion_response, correct, index, block, image, rt)
12
- return(df)
13
- }
14
-
15
- emotion_analyze_performance <- function(df) {
16
- df_out <- df %>%
17
- group_by(block) %>%
18
- summarise(correct = sum(correct) / n(),
19
- mean_rt = mean(rt),
20
- .groups = "drop") %>%
21
- rowwise() %>%
22
- # We are making the performance metric to be the inverse of the correct
23
- # so that lower values are always better (mean RT is better lower)
24
- mutate(performance = ifelse(block == "quick", 1-correct, mean_rt/correct)) %>%
25
- ungroup()
26
- return(df_out)
27
- }
28
-
29
- emotions_extract_emotion <- function(df) {
30
- URL <- "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/"
31
- df <- df %>%
32
- mutate(
33
- image = emotion,
34
- emotion = gsub(URL, "", emotion),
35
- emotion = gsub(".png", "", emotion),
36
- emotion = gsub("_\\d+", "", emotion)
37
- )
38
- return(df)
39
- }
1
+ emotion_create_trial_table <- function(txt) {
2
+ emotion_values <- c(`0` = "neutral", `1` = "sad", `2` = "happy",
3
+ `3` = "surprised", `4` = "angry")
4
+ df <- results_to_df(txt) %>%
5
+ emotions_extract_emotion() %>%
6
+ filter(!is.na(response), !is.na(emotion)) %>%
7
+ mutate(emotion_response = emotion_values[response + 1],
8
+ correct = emotion == emotion_response,
9
+ index = 1:n(),
10
+ block = c(rep("regular", 15), rep("quick", 15), rep("rotated", 15))) %>%
11
+ select(emotion, emotion_response, correct, index, block, image, rt)
12
+ return(df)
13
+ }
14
+
15
+ emotion_analyze_performance <- function(df) {
16
+ df_out <- df %>%
17
+ group_by(block) %>%
18
+ summarise(correct = sum(correct) / n(),
19
+ mean_rt = mean(rt),
20
+ .groups = "drop") %>%
21
+ rowwise() %>%
22
+ # We are making the performance metric to be the inverse of the correct
23
+ # so that lower values are always better (mean RT is better lower)
24
+ mutate(performance = ifelse(block == "quick", 1-correct, mean_rt/correct)) %>%
25
+ ungroup()
26
+ return(df_out)
27
+ }
28
+
29
+ emotions_extract_emotion <- function(df) {
30
+ URL <- "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/"
31
+ df <- df %>%
32
+ mutate(
33
+ image = emotion,
34
+ emotion = gsub(URL, "", emotion),
35
+ emotion = gsub(".png", "", emotion),
36
+ emotion = gsub("_\\d+", "", emotion)
37
+ )
38
+ return(df)
39
+ }
package/original/gonogo.R CHANGED
@@ -1,32 +1,32 @@
1
- gonogo_create_trial_table <- function(txt) {
2
- arr <- fromJSON(txt)
3
- df <- as.data.frame(fromJSON(arr[[1]]))
4
- GONOGO_IMAGE_PATH <- "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/gonogo/"
5
- df <- filter(df, !is.na(image))
6
- df <- select(df, trial_index, time_elapsed, rt,
7
- response, block_number, image)
8
- df$image <- gsub(GONOGO_IMAGE_PATH, "", df$image)
9
- df$image <- gsub(".png", "", df$image)
10
- df <- df %>%
11
- mutate(
12
- should_go = case_when(
13
- block_number == 1 & grepl("astronaut_trans_1", image) ~ TRUE,
14
- block_number == 2 & grepl("alien_trans_1", image) ~ TRUE,
15
- block_number == 3 & grepl("rocket_trans_1", image) ~ TRUE,
16
- block_number == 4 & !grepl("rocket_trans_1", image) ~ TRUE,
17
- TRUE ~ FALSE),
18
- did_go = !is.na(response),
19
- correct = should_go == did_go
20
- )
21
- return(df)
22
- }
23
-
24
- gonogo_analyze_performance <- function(df) {
25
- df_out <- df %>%
26
- group_by(block = block_number) %>%
27
- summarise(correct = sum(correct) / n(),
28
- mean_rt = mean(rt[should_go], na.rm = TRUE),
29
- performance = mean_rt / correct,
30
- .groups = "drop")
31
- return(df_out)
32
- }
1
+ gonogo_create_trial_table <- function(txt) {
2
+ arr <- fromJSON(txt)
3
+ df <- as.data.frame(fromJSON(arr[[1]]))
4
+ GONOGO_IMAGE_PATH <- "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/gonogo/"
5
+ df <- filter(df, !is.na(image))
6
+ df <- select(df, trial_index, time_elapsed, rt,
7
+ response, block_number, image)
8
+ df$image <- gsub(GONOGO_IMAGE_PATH, "", df$image)
9
+ df$image <- gsub(".png", "", df$image)
10
+ df <- df %>%
11
+ mutate(
12
+ should_go = case_when(
13
+ block_number == 1 & grepl("astronaut_trans_1", image) ~ TRUE,
14
+ block_number == 2 & grepl("alien_trans_1", image) ~ TRUE,
15
+ block_number == 3 & grepl("rocket_trans_1", image) ~ TRUE,
16
+ block_number == 4 & !grepl("rocket_trans_1", image) ~ TRUE,
17
+ TRUE ~ FALSE),
18
+ did_go = !is.na(response),
19
+ correct = should_go == did_go
20
+ )
21
+ return(df)
22
+ }
23
+
24
+ gonogo_analyze_performance <- function(df) {
25
+ df_out <- df %>%
26
+ group_by(block = block_number) %>%
27
+ summarise(correct = sum(correct) / n(),
28
+ mean_rt = mean(rt[should_go], na.rm = TRUE),
29
+ performance = mean_rt / correct,
30
+ .groups = "drop")
31
+ return(df_out)
32
+ }
package/package.json CHANGED
@@ -1,40 +1,40 @@
1
- {
2
- "name": "compileprofile-spacetests",
3
- "version": "0.3.0",
4
- "description": "A package for evaluating cognitive test results from ",
5
- "main": "dist/cjs/index.js",
6
- "module": "dist/esm/index.js",
7
- "types": "dist/cjs/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "import": "./dist/esm/index.js",
11
- "require": "./dist/cjs/index.js"
12
- }
13
- },
14
- "scripts": {
15
- "build": "npm run build:csj && npm run build:esm",
16
- "build:csj": "tsc --project tsconfig.cjs.json",
17
- "build:esm": "tsc --project tsconfig.esm.json",
18
- "test": "jest",
19
- "output gonogo": "node dist/index.js",
20
- "prepare": "npm run build"
21
- },
22
- "keywords": [
23
- "cognitive",
24
- "testing",
25
- "memory",
26
- "evaluation"
27
- ],
28
- "author": "",
29
- "license": "MIT",
30
- "devDependencies": {
31
- "@types/jest": "^29.5.12",
32
- "@types/node": "^20.11.24",
33
- "jest": "^29.7.0",
34
- "ts-jest": "^29.1.2",
35
- "typescript": "^5.3.3"
36
- },
37
- "dependencies": {
38
- "mathjs": "^14.4.0"
39
- }
40
- }
1
+ {
2
+ "name": "compileprofile-spacetests",
3
+ "version": "0.4.0",
4
+ "description": "A package for evaluating cognitive test results from ",
5
+ "main": "dist/cjs/index.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/cjs/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/esm/index.js",
11
+ "require": "./dist/cjs/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "npm run build:csj && npm run build:esm",
16
+ "build:csj": "tsc --project tsconfig.cjs.json",
17
+ "build:esm": "tsc --project tsconfig.esm.json",
18
+ "test": "jest",
19
+ "output gonogo": "node dist/index.js",
20
+ "prepare": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "cognitive",
24
+ "testing",
25
+ "memory",
26
+ "evaluation"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "devDependencies": {
31
+ "@types/jest": "^29.5.12",
32
+ "@types/node": "^20.11.24",
33
+ "jest": "^29.7.0",
34
+ "ts-jest": "^29.1.2",
35
+ "typescript": "^5.3.3"
36
+ },
37
+ "dependencies": {
38
+ "mathjs": "^14.4.0"
39
+ }
40
+ }
@@ -1,17 +1,17 @@
1
- import { getBrowserInformation } from '../../getters';
2
- import { loadTestData } from '../test-utils/testData';
3
-
4
- describe('Browser Information Integration Tests', () => {
5
- const testData = loadTestData('nback');
6
-
7
- // Test with spatial cognition data
8
- it('should extract browser info from spatial cognition data', () => {
9
- const browserInfo = getBrowserInformation(testData.nback);
10
- // expect browserInfo to be an object with the following properties
11
- expect(browserInfo).toMatchObject({
12
- browser: expect.any(String),
13
- os: expect.any(String),
14
- mobile: expect.any(Boolean)
15
- });
16
- });
1
+ import { getBrowserInformation } from '../../getters';
2
+ import { loadTestData } from '../test-utils/testData';
3
+
4
+ describe('Browser Information Integration Tests', () => {
5
+ const testData = loadTestData('nback');
6
+
7
+ // Test with spatial cognition data
8
+ it('should extract browser info from spatial cognition data', () => {
9
+ const browserInfo = getBrowserInformation(testData.nback);
10
+ // expect browserInfo to be an object with the following properties
11
+ expect(browserInfo).toMatchObject({
12
+ browser: expect.any(String),
13
+ os: expect.any(String),
14
+ mobile: expect.any(Boolean)
15
+ });
16
+ });
17
17
  });
@@ -1,173 +1,173 @@
1
- import { emotion_extract_trials, emotion_results,
2
- emotion_calculate_results } from '../../emotions';
3
- import { loadTestData } from '../test-utils/testData';
4
-
5
- describe('Emotions test module', () => {
6
- const emotionsData = loadTestData('emotions').emotions;
7
-
8
- // Comprehensive mock data for predictable testing
9
- const mockEmotionsData = [
10
- {
11
- "index": 1,
12
- "emotion": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/surprised_3.png",
13
- "rt": 1000,
14
- "stimulus": "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/surprised_3.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Mapa\">",
15
- "response": 3,
16
- "trial_type": "html-button-response",
17
- "trial_index": 9,
18
- "plugin_version": "2.0.0",
19
- "time_elapsed": 45416,
20
- "attempted": false
21
- },
22
- {
23
- "index": 2,
24
- "emotion": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/sad_1.png",
25
- "rt": 1000,
26
- "stimulus": "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/sad_1.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Mapa\">",
27
- "response": 1,
28
- "trial_type": "html-button-response",
29
- "trial_index": 10,
30
- "plugin_version": "2.0.0",
31
- "time_elapsed": 47656,
32
- "attempted": false
33
- },
34
- {
35
- "index": 3,
36
- "emotion": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/angry_1.png",
37
- "rt": 2000,
38
- "stimulus": "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/angry_1.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Mapa\">",
39
- "response": 3,
40
- "trial_type": "html-button-response",
41
- "trial_index": 11,
42
- "plugin_version": "2.0.0",
43
- "time_elapsed": 49698,
44
- "attempted": false
45
- },
46
- {
47
- "index": 4,
48
- "emotion": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/sad_3.png",
49
- "rt": 1000,
50
- "stimulus": "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/sad_3.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Mapa\">",
51
- "response": 1,
52
- "trial_type": "html-button-response",
53
- "trial_index": 12,
54
- "plugin_version": "2.0.0",
55
- "time_elapsed": 52944,
56
- "attempted": false
57
- },
58
- // Non-emotion data that should be filtered out
59
- { trial_type: 'browser-check', browser: 'safari' }
60
- ];
61
-
62
- describe('emotion_extract_trials', () => {
63
- it('should extract trials from raw data', () => {
64
- const trials = emotion_extract_trials(emotionsData);
65
- expect(trials.length).toBeLessThan(emotionsData.length);
66
- trials.forEach(trial => {
67
- expect(trial).toHaveProperty('rt');
68
- expect(trial).toHaveProperty('emotion');
69
- expect(trial).toHaveProperty('emotion_response');
70
- expect(trial).toHaveProperty('correct');
71
- // property correct should be boolean
72
- expect(typeof trial.correct).toBe('boolean');
73
- expect(trial).toHaveProperty('image_number');
74
- expect(typeof trial.emotion).toBe('string');
75
- });
76
- });
77
-
78
- it('should handle empty data array', () => {
79
- const trials = emotion_extract_trials([]);
80
- expect(trials).toEqual([]);
81
- });
82
-
83
- it('should extract mock data correctly', () => {
84
- const trials = emotion_extract_trials(mockEmotionsData);
85
- expect(trials.length).toBe(4); // Only trials with valid emotion, rt, and response
86
-
87
- // Check emotion extraction and response mapping
88
- expect(trials[0].emotion).toBe('surprised');
89
- expect(trials[0].image_number).toBe('3');
90
- expect(trials[0].emotion_response).toBe('surprised');
91
- expect(trials[0].correct).toBe(true);
92
-
93
- expect(trials[1].emotion).toBe('sad');
94
- expect(trials[1].emotion_response).toBe('sad');
95
- expect(trials[1].correct).toBe(true);
96
-
97
- expect(trials[2].emotion).toBe('angry');
98
- expect(trials[2].emotion_response).toBe('surprised');
99
- expect(trials[2].correct).toBe(false);
100
- });
101
- });
102
-
103
- describe('emotion_results', () => {
104
- it('should calculate correct averages', () => {
105
- // Create sample trial data
106
- const sampleTrials = [
107
- { rt: 600, correct: true, emotion: 'happy', emotion_response: 'happy' },
108
- { rt: 1000, correct: true, emotion: 'sad', emotion_response: 'sad' },
109
- { rt: 800, correct: false, emotion: 'angry', emotion_response: 'happy' },
110
- { rt: 400, correct: false, emotion: 'sad', emotion_response: 'happy' },
111
- ];
112
-
113
- const results = emotion_results(sampleTrials);
114
-
115
- expect(results).toHaveProperty('common');
116
- expect(results.common.n_trials).toBe(4);
117
- expect(results.common.avg_rt_total).toBe(700); // (600 + 1000 + 800 + 400) / 4
118
- expect(results.common.n_correct_trials / results.common.n_trials).toBe(2/4); // (1 + 1 + 0 + 0) / 4
119
- expect(results.common.performance_score).toBeGreaterThanOrEqual(0);
120
- expect(results.common.performance_score).toBeLessThanOrEqual(100);
121
- expect(results).toHaveProperty('special');
122
- expect(results.special).toHaveProperty('by_emotion');
123
- expect(results.special).toHaveProperty('confusion_matrix');
124
- });
125
-
126
- it('should handle empty trials array', () => {
127
- // When given an empty array, it should handle it gracefully
128
- expect(() => emotion_results([])).toThrow();
129
- });
130
-
131
- it('should calculate mock data results correctly', () => {
132
- const mockTrials = emotion_extract_trials(mockEmotionsData);
133
- const mockResults = emotion_results(mockTrials);
134
-
135
- // Expected: 5 trials total, 4 correct, 1 incorrect, 0 missing (missing was filtered out)
136
- expect(mockResults.common.n_trials).toBe(4);
137
- expect(mockResults.common.n_correct_trials).toBe(3);
138
- expect(mockResults.common.n_incorrect_trials).toBe(1);
139
-
140
- expect(mockResults.common.avg_rt_total).toBe(1250);
141
- expect(mockResults.common.avg_rt_correct).toBe(1000);
142
- expect(mockResults.common.avg_rt_incorrect).toBe(2000);
143
-
144
- // Performance score calculation using RT/(accuracy^5)
145
- expect(mockResults.common.performance_score).toBeGreaterThan(0);
146
- expect(mockResults.common.performance_score).toBeLessThanOrEqual(100);
147
-
148
- // Check special results structure
149
- expect(mockResults.special.by_emotion).toHaveLength(5); // neutral, sad, happy, surprised, angry
150
- expect(mockResults.special.confusion_matrix).toHaveProperty('happy');
151
- expect(mockResults.special.confusion_matrix).toHaveProperty('sad');
152
- expect(mockResults.special.confusion_matrix).toHaveProperty('angry');
153
- expect(mockResults.special.confusion_matrix).toHaveProperty('neutral');
154
- expect(mockResults.special.confusion_matrix).toHaveProperty('surprised');
155
- });
156
- });
157
-
158
- describe('emotion_calculate_results', () => {
159
- it('should process raw data and return valid results', () => {
160
- const results = emotion_calculate_results(emotionsData);
161
- console.log(results);
162
- expect(results).toHaveProperty('common');
163
- expect(results).toHaveProperty('special');
164
- expect(results.common).toHaveProperty('n_trials');
165
- expect(results.common).toHaveProperty('n_correct_trials');
166
- expect(results.common).toHaveProperty('avg_rt_total');
167
- expect(results.common).toHaveProperty('performance_score');
168
-
169
- expect(results.common.performance_score).toBeGreaterThan(0);
170
- expect(results.common.performance_score).toBeLessThanOrEqual(100);
171
- });
172
- });
1
+ import { emotion_extract_trials, emotion_results,
2
+ emotion_calculate_results } from '../../emotions';
3
+ import { loadTestData } from '../test-utils/testData';
4
+
5
+ describe('Emotions test module', () => {
6
+ const emotionsData = loadTestData('emotions').emotions;
7
+
8
+ // Comprehensive mock data for predictable testing
9
+ const mockEmotionsData = [
10
+ {
11
+ "index": 1,
12
+ "emotion": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/surprised_3.png",
13
+ "rt": 1000,
14
+ "stimulus": "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/surprised_3.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Mapa\">",
15
+ "response": 3,
16
+ "trial_type": "html-button-response",
17
+ "trial_index": 9,
18
+ "plugin_version": "2.0.0",
19
+ "time_elapsed": 45416,
20
+ "attempted": false
21
+ },
22
+ {
23
+ "index": 2,
24
+ "emotion": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/sad_1.png",
25
+ "rt": 1000,
26
+ "stimulus": "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/sad_1.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Mapa\">",
27
+ "response": 1,
28
+ "trial_type": "html-button-response",
29
+ "trial_index": 10,
30
+ "plugin_version": "2.0.0",
31
+ "time_elapsed": 47656,
32
+ "attempted": false
33
+ },
34
+ {
35
+ "index": 3,
36
+ "emotion": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/angry_1.png",
37
+ "rt": 2000,
38
+ "stimulus": "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/angry_1.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Mapa\">",
39
+ "response": 3,
40
+ "trial_type": "html-button-response",
41
+ "trial_index": 11,
42
+ "plugin_version": "2.0.0",
43
+ "time_elapsed": 49698,
44
+ "attempted": false
45
+ },
46
+ {
47
+ "index": 4,
48
+ "emotion": "https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/sad_3.png",
49
+ "rt": 1000,
50
+ "stimulus": "<img src=\"https://unyavwofnmwihnpvkjji.supabase.co/storage/v1/object/public/test-stimuli/emotions//final/sad_3.png\" class=\"max-w-none rotate_0\" width=\"512\" height=\"512\" alt=\"Mapa\">",
51
+ "response": 1,
52
+ "trial_type": "html-button-response",
53
+ "trial_index": 12,
54
+ "plugin_version": "2.0.0",
55
+ "time_elapsed": 52944,
56
+ "attempted": false
57
+ },
58
+ // Non-emotion data that should be filtered out
59
+ { trial_type: 'browser-check', browser: 'safari' }
60
+ ];
61
+
62
+ describe('emotion_extract_trials', () => {
63
+ it('should extract trials from raw data', () => {
64
+ const trials = emotion_extract_trials(emotionsData);
65
+ expect(trials.length).toBeLessThan(emotionsData.length);
66
+ trials.forEach(trial => {
67
+ expect(trial).toHaveProperty('rt');
68
+ expect(trial).toHaveProperty('emotion');
69
+ expect(trial).toHaveProperty('emotion_response');
70
+ expect(trial).toHaveProperty('correct');
71
+ // property correct should be boolean
72
+ expect(typeof trial.correct).toBe('boolean');
73
+ expect(trial).toHaveProperty('image_number');
74
+ expect(typeof trial.emotion).toBe('string');
75
+ });
76
+ });
77
+
78
+ it('should handle empty data array', () => {
79
+ const trials = emotion_extract_trials([]);
80
+ expect(trials).toEqual([]);
81
+ });
82
+
83
+ it('should extract mock data correctly', () => {
84
+ const trials = emotion_extract_trials(mockEmotionsData);
85
+ expect(trials.length).toBe(4); // Only trials with valid emotion, rt, and response
86
+
87
+ // Check emotion extraction and response mapping
88
+ expect(trials[0].emotion).toBe('surprised');
89
+ expect(trials[0].image_number).toBe('3');
90
+ expect(trials[0].emotion_response).toBe('surprised');
91
+ expect(trials[0].correct).toBe(true);
92
+
93
+ expect(trials[1].emotion).toBe('sad');
94
+ expect(trials[1].emotion_response).toBe('sad');
95
+ expect(trials[1].correct).toBe(true);
96
+
97
+ expect(trials[2].emotion).toBe('angry');
98
+ expect(trials[2].emotion_response).toBe('surprised');
99
+ expect(trials[2].correct).toBe(false);
100
+ });
101
+ });
102
+
103
+ describe('emotion_results', () => {
104
+ it('should calculate correct averages', () => {
105
+ // Create sample trial data
106
+ const sampleTrials = [
107
+ { rt: 600, correct: true, emotion: 'happy', emotion_response: 'happy' },
108
+ { rt: 1000, correct: true, emotion: 'sad', emotion_response: 'sad' },
109
+ { rt: 800, correct: false, emotion: 'angry', emotion_response: 'happy' },
110
+ { rt: 400, correct: false, emotion: 'sad', emotion_response: 'happy' },
111
+ ];
112
+
113
+ const results = emotion_results(sampleTrials);
114
+
115
+ expect(results).toHaveProperty('common');
116
+ expect(results.common.n_trials).toBe(4);
117
+ expect(results.common.avg_rt_total).toBe(700); // (600 + 1000 + 800 + 400) / 4
118
+ expect(results.common.n_correct_trials / results.common.n_trials).toBe(2/4); // (1 + 1 + 0 + 0) / 4
119
+ expect(results.common.performance_score).toBeGreaterThanOrEqual(0);
120
+ expect(results.common.performance_score).toBeLessThanOrEqual(100);
121
+ expect(results).toHaveProperty('special');
122
+ expect(results.special).toHaveProperty('by_emotion');
123
+ expect(results.special).toHaveProperty('confusion_matrix');
124
+ });
125
+
126
+ it('should handle empty trials array', () => {
127
+ // When given an empty array, it should handle it gracefully
128
+ expect(() => emotion_results([])).toThrow();
129
+ });
130
+
131
+ it('should calculate mock data results correctly', () => {
132
+ const mockTrials = emotion_extract_trials(mockEmotionsData);
133
+ const mockResults = emotion_results(mockTrials);
134
+
135
+ // Expected: 5 trials total, 4 correct, 1 incorrect, 0 missing (missing was filtered out)
136
+ expect(mockResults.common.n_trials).toBe(4);
137
+ expect(mockResults.common.n_correct_trials).toBe(3);
138
+ expect(mockResults.common.n_incorrect_trials).toBe(1);
139
+
140
+ expect(mockResults.common.avg_rt_total).toBe(1250);
141
+ expect(mockResults.common.avg_rt_correct).toBe(1000);
142
+ expect(mockResults.common.avg_rt_incorrect).toBe(2000);
143
+
144
+ // Performance score calculation using RT/(accuracy^5)
145
+ expect(mockResults.common.performance_score).toBeGreaterThan(0);
146
+ expect(mockResults.common.performance_score).toBeLessThanOrEqual(100);
147
+
148
+ // Check special results structure
149
+ expect(mockResults.special.by_emotion).toHaveLength(5); // neutral, sad, happy, surprised, angry
150
+ expect(mockResults.special.confusion_matrix).toHaveProperty('happy');
151
+ expect(mockResults.special.confusion_matrix).toHaveProperty('sad');
152
+ expect(mockResults.special.confusion_matrix).toHaveProperty('angry');
153
+ expect(mockResults.special.confusion_matrix).toHaveProperty('neutral');
154
+ expect(mockResults.special.confusion_matrix).toHaveProperty('surprised');
155
+ });
156
+ });
157
+
158
+ describe('emotion_calculate_results', () => {
159
+ it('should process raw data and return valid results', () => {
160
+ const results = emotion_calculate_results(emotionsData);
161
+ console.log(results);
162
+ expect(results).toHaveProperty('common');
163
+ expect(results).toHaveProperty('special');
164
+ expect(results.common).toHaveProperty('n_trials');
165
+ expect(results.common).toHaveProperty('n_correct_trials');
166
+ expect(results.common).toHaveProperty('avg_rt_total');
167
+ expect(results.common).toHaveProperty('performance_score');
168
+
169
+ expect(results.common.performance_score).toBeGreaterThan(0);
170
+ expect(results.common.performance_score).toBeLessThanOrEqual(100);
171
+ });
172
+ });
173
173
  });