#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10;
const int INF = 1e9;
int vis[MAXN], edge[MAXN], out[MAXN];
pair<int, pair<int, int>> pai[MAXN]; // duas arestas "pai" do vértice
set<pair<int, pair<int, int>>> adj[MAXN], inv[MAXN];
// guardo o inv para poder desfazer adj depois
bool cycle = false;
vector<int> ans;
void get_cycle(int v, int w, int i, int j){
// vou de v para w (usando os pais) na ordem reversa,
// depois adiciono i e j
while(v != w){
ans.push_back(pai[v].second.first);
ans.push_back(pai[v].second.second);
v = pai[v].first;
}
reverse(ans.begin(), ans.end());
ans.push_back(i);
ans.push_back(j);
}
void dfs(int v){
// cout << "dfs at " << v << endl;
vis[v] = 1;
for(auto [c1, x] : adj[v]){
// cout << "looking at " << x.first << endl;
// cout << "possible candidates: ";
// for(auto [c2, x2] : adj[x.first]) cout << x2.first << " "; cout << endl;
auto [u, i] = x;
for(auto [c2, y] : adj[u]){
auto [w, j] = y;
if(c1 == c2) continue;
if(!vis[w]){
pai[w] = {v, {j, i}}; // preciso guardar as arestas que usei
dfs(w);
} else if(!out[w]){
// eba, encontrei um ciclo!
if(!cycle) get_cycle(v, w, i, j);
cycle = true;
}
}
}
out[v] = 1;
}
void solve(){
int n, m; cin >> n >> m;
cycle = false;
ans.clear();
for(int i=1; i<=n; i++){
adj[i].clear(); inv[i].clear();
vis[i] = out[i] = 0;
}
for(int i=0; i<m; i++){
int a, b, c; cin >> a >> b >> c;
adj[a].insert({c, {b, i}});
inv[b].insert({c, {a, i}});
edge[i] = 0;
}
for(int i=1; i<=n; i++) if(!vis[i]) dfs(i);
if(!cycle){
cout << "NO\n";
return;
}
cout << "YES\n";
for(auto i : ans) edge[i] ++;
while(edge[ans.back()] > 1){
edge[ans.back()] --;
ans.pop_back();
}
cout << (int) ans.size() << " ";
for(auto i : ans) cout << i + 1 << " ";
cout << "\n";
}
int main(){
cin.tie(0)->sync_with_stdio(0);
int t; cin >> t;
while(t--){
solve();
}
return 0;
}