1
1
// eslint-disable-next-line import/no-internal-modules
2
2
import type { AllureRuntime } from 'jest-allure2-reporter/api' ;
3
3
4
- import type { Emitter , AndroidEntry , IosEntry , Entry } from 'logkitten' ;
4
+ import type { Emitter , AndroidEntry , IosEntry } from 'logkitten' ;
5
5
import { Level , logkitten } from 'logkitten' ;
6
6
import type { DetoxAllure2AdapterDeviceLogsOptions } from '../types' ;
7
7
import type { DeviceWrapper } from '../utils' ;
8
8
9
+ import { PIDEntryCollection } from './pid-entry-collection' ;
10
+
9
11
type AnyEntry = AndroidEntry & IosEntry ;
10
12
11
13
export interface LogBufferOptions {
@@ -28,9 +30,8 @@ const noop = () => {};
28
30
29
31
export class LogBuffer implements StepLogRecorder {
30
32
private readonly _emitter : Emitter ;
31
- private _entries : AnyEntry [ ] = [ ] ;
32
- private _purgatory : AnyEntry [ ] = [ ] ;
33
- private _pid = Number . NaN ;
33
+ private readonly _appEntries = new PIDEntryCollection ( ) ;
34
+ private readonly _detoxEntries = new PIDEntryCollection ( ) ;
34
35
private _options : DetoxAllure2AdapterDeviceLogsOptions ;
35
36
36
37
constructor ( readonly _config : LogBufferOptions ) {
@@ -57,41 +58,14 @@ export class LogBuffer implements StepLogRecorder {
57
58
}
58
59
59
60
public resetPid ( ) {
60
- this . _pid = Number . NaN ;
61
+ this . _appEntries . pid = Number . NaN ;
62
+ this . _detoxEntries . pid = Number . NaN ;
61
63
}
62
64
63
65
public refreshPid ( ) {
64
- this . _pid = this . _config . device . getPid ( ) ;
65
-
66
- if ( Number . isFinite ( this . _pid ) && this . _purgatory . length > 0 ) {
67
- this . _entries = [ ...this . _entries , ...this . _purgatory . splice ( 0 ) . filter ( this . _matchesPid ) ] ;
68
- }
69
- }
70
-
71
- public flush ( failed ?: boolean ) : string {
72
- if ( this . _entries . length === 0 ) {
73
- return '' ;
74
- }
75
-
76
- const entries = this . _entries . splice ( 0 ) ;
77
-
78
- // Check if we should save logs based on failure status
79
- const saveAll = this . _options . saveAll ?? false ;
80
- if ( ! saveAll && ! failed ) {
81
- return '' ;
82
- }
83
-
84
- const result = entries
85
- . map ( ( entry ) => {
86
- const levelLetter = Level [ entry . level as Level ] || 'UNKNOWN' ;
87
- const tagOrCategory = entry . tag || `${ entry . subsystem } :${ entry . category } ` ;
88
- const msg = entry . msg ;
89
-
90
- return `${ levelLetter } \t${ tagOrCategory } \t${ msg } ` ;
91
- } )
92
- . join ( '\n' ) ;
93
-
94
- return result + '\n' ;
66
+ const pid = this . _config . device . getPid ( ) ;
67
+ this . _appEntries . pid = pid ;
68
+ this . _detoxEntries . pid = pid ;
95
69
}
96
70
97
71
public async close ( ) {
@@ -116,25 +90,39 @@ export class LogBuffer implements StepLogRecorder {
116
90
}
117
91
118
92
private readonly _attachLogs = ( allure : AllureRuntime , failed : boolean , after : boolean ) => {
119
- const content = this . flush ( failed ) ;
93
+ // Check if we should save logs based on failure status
94
+ const saveAll = this . _options . saveAll ?? false ;
95
+ if ( ! saveAll && ! failed ) {
96
+ return ;
97
+ }
120
98
121
- if ( content ) {
99
+ const appContent = this . _appEntries . flushAsString ( ) ;
100
+ if ( appContent ) {
122
101
const name = after ? 'app.log' : 'app-before.log' ;
123
- allure . attachment ( name , content , 'text/plain' ) ;
102
+ allure . attachment ( name , appContent , 'text/plain' ) ;
124
103
}
125
- } ;
126
104
127
- private readonly _onEntry = ( entry : AnyEntry ) => {
128
- if ( Number . isFinite ( this . _pid ) ) {
129
- this . _entries . push ( entry ) ;
130
- } else {
131
- this . _purgatory . push ( entry ) ;
105
+ const detoxContent = this . _detoxEntries . flushAsString ( ) ;
106
+ if ( detoxContent ) {
107
+ const name = after ? 'detox.log' : 'detox-before.log' ;
108
+ allure . attachment ( name , detoxContent , 'text/plain' ) ;
132
109
}
133
110
} ;
134
111
135
- private readonly _matchesPid = ( entry : Entry ) => entry . pid === this . _pid ;
112
+ private readonly _onEntry = ( entry : AnyEntry ) => {
113
+ this . _appEntries . push ( entry ) ;
114
+ } ;
136
115
137
116
private _iosFilter ( entry : IosEntry ) : boolean {
117
+ if ( entry . subsystem === 'com.wix.Detox' ) {
118
+ this . _detoxEntries . push ( entry ) ;
119
+
120
+ // Exclude Detox logs from app logs unless they are errors
121
+ if ( entry . level < Level . ERROR ) {
122
+ return false ;
123
+ }
124
+ }
125
+
138
126
const userFilter = this . _options . ios ;
139
127
const override = this . _options . override ;
140
128
if ( ! override && ! this . _defaultIosFilter ( entry ) ) {
@@ -145,6 +133,15 @@ export class LogBuffer implements StepLogRecorder {
145
133
}
146
134
147
135
private _androidFilter ( entry : AndroidEntry ) : boolean {
136
+ if ( entry . tag && entry . tag . startsWith ( 'Detox' ) ) {
137
+ this . _detoxEntries . push ( entry ) ;
138
+
139
+ // Exclude Detox logs from app logs unless they are errors
140
+ if ( entry . level < Level . ERROR ) {
141
+ return false ;
142
+ }
143
+ }
144
+
148
145
const userFilter = this . _options . android ;
149
146
const override = this . _options . override ;
150
147
if ( ! override && ! this . _defaultAndroidFilter ( entry ) ) {
@@ -155,10 +152,19 @@ export class LogBuffer implements StepLogRecorder {
155
152
}
156
153
157
154
private readonly _defaultIosFilter = ( entry : IosEntry ) => {
155
+ // Only handle React Native app logs, not Detox logs
158
156
if ( entry . subsystem . startsWith ( 'com.facebook.react.' ) ) {
157
+ if ( entry . msg . startsWith ( 'Unbalanced calls start/end for tag' ) ) {
158
+ return false ;
159
+ }
160
+
159
161
return true ;
160
162
}
161
163
164
+ if ( entry . processImagePath . endsWith ( '/proactiveeventtrackerd' ) ) {
165
+ return false ;
166
+ }
167
+
162
168
if ( entry . level >= Level . ERROR ) {
163
169
return ! entry . subsystem . startsWith ( 'com.apple.' ) ; // && !entry.msg.includes('(CFNetwork)');
164
170
}
@@ -167,7 +173,8 @@ export class LogBuffer implements StepLogRecorder {
167
173
} ;
168
174
169
175
private readonly _defaultAndroidFilter = ( entry : AndroidEntry ) => {
170
- if ( entry . tag . startsWith ( 'Detox' ) || entry . tag . startsWith ( 'React' ) ) {
176
+ // Only handle React Native app logs, not Detox logs
177
+ if ( entry . tag . startsWith ( 'React' ) ) {
171
178
return true ;
172
179
}
173
180
0 commit comments