mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	We now treat util like a leaf module and use "require" to import it everywhere it's used. An earlier version of this commit moved util into our "shared" library, but we decided to wait on that. Once we're ready to do that, we should only need to do a simple search/replace on various require/zrequire statements plus a small tweak to one of the custom linter checks. It turns out we don't really need util.js for our most immediate code-sharing goal, which is to reuse our markdown code on mobile. There's a little bit of cleanup still remaining to break the dependency, but it's minor. The util module still calls the global blueslip module in one place, but that code is about to be removed in the next few commits. I am pretty confident that once we start sharing things like the typeahead code more aggressively, we'll start having dependencies on util. The module is barely more than 300 lines long, so we'll probably just move the whole thing into shared rather than break it apart. Also, we can continue to nibble away at the cruftier parts of the module.
		
			
				
	
	
		
			482 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			482 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
set_global('i18n', global.stub_i18n);
 | 
						|
 | 
						|
zrequire('Filter', 'js/filter');
 | 
						|
zrequire('MessageListData', 'js/message_list_data');
 | 
						|
zrequire('narrow_state');
 | 
						|
zrequire('narrow');
 | 
						|
zrequire('stream_data');
 | 
						|
 | 
						|
set_global('message_list', {});
 | 
						|
set_global('muting', {
 | 
						|
    is_topic_muted: () => false,
 | 
						|
});
 | 
						|
 | 
						|
function test_with(fixture) {
 | 
						|
    const filter = new Filter(fixture.filter_terms);
 | 
						|
    narrow_state.set_current_filter(filter);
 | 
						|
 | 
						|
    // Make sure our simulated tests data satisfies the
 | 
						|
    // invarariant that the first unread message we find
 | 
						|
    // does indeed satisfy our filter.
 | 
						|
    if (fixture.unread_info.flavor === 'found') {
 | 
						|
        for (const msg of fixture.all_messages) {
 | 
						|
            if (msg.id === fixture.unread_info.msg_id) {
 | 
						|
                assert(filter.predicate()(msg));
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    const muting_enabled = narrow_state.muting_enabled();
 | 
						|
    const msg_data = new MessageListData({
 | 
						|
        filter: narrow_state.filter(),
 | 
						|
        muting_enabled: muting_enabled,
 | 
						|
    });
 | 
						|
    const id_info = {
 | 
						|
        target_id: fixture.target_id,
 | 
						|
        local_select_id: undefined,
 | 
						|
        final_select_id: undefined,
 | 
						|
    };
 | 
						|
 | 
						|
    message_list.all = {
 | 
						|
        fetch_status: {
 | 
						|
            has_found_newest: () => fixture.has_found_newest,
 | 
						|
        },
 | 
						|
        empty: () => fixture.empty,
 | 
						|
        all_messages: () => {
 | 
						|
            assert(fixture.all_messages !== undefined);
 | 
						|
            return fixture.all_messages;
 | 
						|
        },
 | 
						|
        first: () => {
 | 
						|
            assert(fixture.all_messages !== undefined);
 | 
						|
            return fixture.all_messages[0];
 | 
						|
        },
 | 
						|
        last: () => {
 | 
						|
            assert(fixture.all_messages !== undefined);
 | 
						|
            return fixture.all_messages[fixture.all_messages.length - 1];
 | 
						|
        },
 | 
						|
    };
 | 
						|
 | 
						|
    narrow_state.get_first_unread_info = () => fixture.unread_info;
 | 
						|
 | 
						|
    narrow.maybe_add_local_messages({
 | 
						|
        id_info: id_info,
 | 
						|
        msg_data: msg_data,
 | 
						|
    });
 | 
						|
 | 
						|
    assert.deepEqual(id_info, fixture.expected_id_info);
 | 
						|
 | 
						|
    const msgs = msg_data.all_messages();
 | 
						|
    const msg_ids = _.pluck(msgs, 'id');
 | 
						|
    assert.deepEqual(msg_ids, fixture.expected_msg_ids);
 | 
						|
}
 | 
						|
 | 
						|
run_test('near after unreads', () => {
 | 
						|
    // Current near: behavior is to ignore the unreads and take you
 | 
						|
    // to the target message, with reading disabled.
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'near', operand: 42},
 | 
						|
        ],
 | 
						|
        target_id: 42,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'found',
 | 
						|
            msg_id: 37,
 | 
						|
        },
 | 
						|
        has_found_newest: false,
 | 
						|
        all_messages: [
 | 
						|
            {id: 37, topic: 'whatever'},
 | 
						|
            {id: 42, topic: 'whatever'},
 | 
						|
            {id: 44, topic: 'whatever'},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 42,
 | 
						|
            final_select_id: 42,
 | 
						|
            local_select_id: 42,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [37, 42, 44],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('near not in message list', () => {
 | 
						|
    // Current behavior is to ignore the unreads and take you
 | 
						|
    // to the closest messages, with reading disabled.
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'near', operand: 42},
 | 
						|
        ],
 | 
						|
        target_id: 42,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'found',
 | 
						|
            msg_id: 46,
 | 
						|
        },
 | 
						|
        has_found_newest: false,
 | 
						|
        all_messages: [
 | 
						|
            {id: 41, topic: 'whatever'},
 | 
						|
            {id: 45, topic: 'whatever'},
 | 
						|
            {id: 46, topic: 'whatever'},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 42,
 | 
						|
            final_select_id: 42,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [41, 45, 46],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('near before unreads', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'near', operand: 42},
 | 
						|
        ],
 | 
						|
        target_id: 42,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'found',
 | 
						|
            msg_id: 43,
 | 
						|
        },
 | 
						|
        has_found_newest: false,
 | 
						|
        all_messages: [
 | 
						|
            {id: 42, topic: 'whatever'},
 | 
						|
            {id: 43, topic: 'whatever'},
 | 
						|
            {id: 44, topic: 'whatever'},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 42,
 | 
						|
            final_select_id: 42,
 | 
						|
            local_select_id: 42,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [42, 43, 44],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('near with no unreads', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'near', operand: 42},
 | 
						|
        ],
 | 
						|
        target_id: 42,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'not_found',
 | 
						|
        },
 | 
						|
        has_found_newest: false,
 | 
						|
        empty: true,
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 42,
 | 
						|
            final_select_id: 42,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('is private with no target', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'is', operand: 'private'},
 | 
						|
        ],
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'found',
 | 
						|
            msg_id: 550,
 | 
						|
        },
 | 
						|
        has_found_newest: true,
 | 
						|
        all_messages: [
 | 
						|
            {id: 450, type: 'private'},
 | 
						|
            {id: 500, type: 'private'},
 | 
						|
            {id: 550, type: 'private'},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: undefined,
 | 
						|
            final_select_id: 550,
 | 
						|
            local_select_id: 550,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [450, 500, 550],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('pm-with with target outside of range', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'pm-with', operand: 'alice@example.com'},
 | 
						|
        ],
 | 
						|
        target_id: 5,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'not_found',
 | 
						|
        },
 | 
						|
        has_found_newest: false,
 | 
						|
        all_messages: [
 | 
						|
            {id: 999},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 5,
 | 
						|
            final_select_id: 5,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('is:private with no unreads before fetch', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'is', operand: 'private'},
 | 
						|
        ],
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'not_found',
 | 
						|
        },
 | 
						|
        has_found_newest: false,
 | 
						|
        empty: true,
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: undefined,
 | 
						|
            final_select_id: undefined,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('is:private with target and no unreads', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'is', operand: 'private'},
 | 
						|
        ],
 | 
						|
        target_id: 450,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'not_found',
 | 
						|
        },
 | 
						|
        has_found_newest: true,
 | 
						|
        empty: false,
 | 
						|
        all_messages: [
 | 
						|
            {id: 350},
 | 
						|
            {id: 400, type: 'private'},
 | 
						|
            {id: 450, type: 'private'},
 | 
						|
            {id: 500, type: 'private'},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 450,
 | 
						|
            final_select_id: 450,
 | 
						|
            local_select_id: 450,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [400, 450, 500],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('is:mentioned with no unreads and no matches', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'is', operand: 'mentioned'},
 | 
						|
        ],
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'not_found',
 | 
						|
        },
 | 
						|
        has_found_newest: true,
 | 
						|
        all_messages: [],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: undefined,
 | 
						|
            final_select_id: undefined,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('is:alerted with no unreads and one match', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'is', operand: 'alerted'},
 | 
						|
        ],
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'not_found',
 | 
						|
        },
 | 
						|
        has_found_newest: true,
 | 
						|
        all_messages: [
 | 
						|
            {id: 55, topic: 'whatever', alerted: true},
 | 
						|
            {id: 57, topic: 'whatever', alerted: false},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: undefined,
 | 
						|
            final_select_id: 55,
 | 
						|
            local_select_id: 55,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [55],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('search', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'search', operand: 'whatever'},
 | 
						|
        ],
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'cannot_compute',
 | 
						|
        },
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: undefined,
 | 
						|
            final_select_id: 10000000000000000,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('search near', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'search', operand: 'whatever'},
 | 
						|
            {operator: 'near', operand: 22},
 | 
						|
        ],
 | 
						|
        target_id: 22,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'cannot_compute',
 | 
						|
        },
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 22,
 | 
						|
            final_select_id: 22,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('stream, no unread, not in all_messages', () => {
 | 
						|
    // This might be something you'd see zooming out from
 | 
						|
    // a muted topic, maybe?  It's possibly this scenario
 | 
						|
    // is somewhat contrived, but we exercise fairly simple
 | 
						|
    // defensive code that just punts when messages aren't in
 | 
						|
    // our new message list.  Note that our target_id is within
 | 
						|
    // the range of all_messages.
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'stream', operand: 'whatever'},
 | 
						|
        ],
 | 
						|
        target_id: 450,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'not_found',
 | 
						|
        },
 | 
						|
        has_found_newest: true,
 | 
						|
        empty: false,
 | 
						|
        all_messages: [
 | 
						|
            {id: 400},
 | 
						|
            {id: 500},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 450,
 | 
						|
            final_select_id: 450,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('search, stream, not in all_messages', () => {
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'search', operand: 'foo'},
 | 
						|
            {operator: 'stream', operand: 'whatever'},
 | 
						|
        ],
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'cannot_compute',
 | 
						|
        },
 | 
						|
        has_found_newest: true,
 | 
						|
        empty: false,
 | 
						|
        all_messages: [
 | 
						|
            {id: 400},
 | 
						|
            {id: 500},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: undefined,
 | 
						|
            final_select_id: 10000000000000000,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('stream/topic not in all_messages', () => {
 | 
						|
    // This is a bit of a corner case, but you could have a scenario
 | 
						|
    // where you've gone way back in a topic (perhaps something that
 | 
						|
    // has been muted a long time) and find an unread message that isn't
 | 
						|
    // actually in message_list.all.
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'stream', operand: 'one'},
 | 
						|
            {operator: 'topic', operand: 'whatever'},
 | 
						|
        ],
 | 
						|
        target_id: 1000,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'found',
 | 
						|
            msg_id: 2,
 | 
						|
        },
 | 
						|
        has_found_newest: true,
 | 
						|
        all_messages: [
 | 
						|
            {id: 900},
 | 
						|
            {id: 1100},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 1000,
 | 
						|
            final_select_id: 2,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 | 
						|
 | 
						|
run_test('final corner case', () => {
 | 
						|
    // This tries to get all the way to the end of
 | 
						|
    // the function (as written now).  The data here
 | 
						|
    // may be completely contrived.
 | 
						|
    const fixture = {
 | 
						|
        filter_terms: [
 | 
						|
            {operator: 'is', operand: 'starred'},
 | 
						|
        ],
 | 
						|
        target_id: 450,
 | 
						|
        unread_info: {
 | 
						|
            flavor: 'not_found',
 | 
						|
        },
 | 
						|
        has_found_newest: true,
 | 
						|
        empty: false,
 | 
						|
        all_messages: [
 | 
						|
            {id: 400, topic: 'whatever'},
 | 
						|
            {id: 425, topic: 'whatever', starred: true},
 | 
						|
            {id: 500, topic: 'whatever'},
 | 
						|
        ],
 | 
						|
        expected_id_info: {
 | 
						|
            target_id: 450,
 | 
						|
            final_select_id: 450,
 | 
						|
            local_select_id: undefined,
 | 
						|
        },
 | 
						|
        expected_msg_ids: [425],
 | 
						|
    };
 | 
						|
 | 
						|
    test_with(fixture);
 | 
						|
});
 |